Czy mogę używać wątków do wykonywania długotrwałych zadań w usługach IIS?

85

W aplikacji ASP.Net użytkownik klika przycisk na stronie internetowej, a następnie tworzy instancję obiektu na serwerze za pośrednictwem modułu obsługi zdarzeń i wywołuje metodę na obiekcie. Metoda przechodzi do systemu zewnętrznego w celu wykonania pewnych czynności i może to chwilę potrwać. Chciałbym więc uruchomić to wywołanie metody w innym wątku, abym mógł zwrócić kontrolę użytkownikowi z komunikatem „Twoje żądanie zostało przesłane”. Jestem dość szczęśliwy, że mogę to zrobić jako odpal i zapomnij, chociaż byłoby jeszcze przyjemniej, gdyby użytkownik mógł nadal odpytywać obiekt pod kątem statusu.

Nie wiem, czy IIS zezwala na działanie mojego wątku, nawet po wygaśnięciu sesji użytkownika. Wyobraź sobie, że użytkownik uruchamia zdarzenie, a my tworzymy instancję obiektu na serwerze i uruchamiamy metodę w nowym wątku. Użytkownik jest zadowolony z komunikatu „Twoja prośba została przesłana” i zamyka przeglądarkę. W końcu ta sesja użytkownika wygaśnie w usługach IIS, ale wątek może nadal działać, wykonując pracę. Czy usługi IIS pozwolą na kontynuowanie działania wątku, czy też zabiją go i pozbędą się obiektu po wygaśnięciu sesji użytkownika?

EDYCJA: Z odpowiedzi i komentarzy wynika, że ​​najlepszym sposobem na to jest przeniesienie długotrwałego przetwarzania poza usługi IIS. Oprócz wszystkiego innego dotyczy to problemu recyklingu domeny aplikacji. W praktyce potrzebuję uruchomić wersję 1 w ograniczonym czasie i muszę pracować w istniejącym frameworku, więc chciałbym uniknąć warstwy usług, stąd chęć po prostu odpalenia wątku wewnątrz IIS. W praktyce „długotrwałe” to tylko kilka minut, a współbieżność na stronie będzie niska, więc powinno być w porządku. Ale następna wersja z pewnością będzie wymagała podzielenia na osobną warstwę usług.

Frans
źródło
1
Czy masz więcej szczegółów na temat tego, co należy przetworzyć i jakie jest środowisko hostingowe? Na przykład, czy możesz zainstalować usługę?
senfo
Zawsze możesz użyć metod programowania Async i Await (Visual Studio 2012+). Znacznie czystsze niż samodzielne zarządzanie wątkami.
SausageFingers

Odpowiedzi:

65

Możesz osiągnąć to, co chcesz, ale zazwyczaj jest to zły pomysł. Niektóre blogi ASP.NET i silniki CMS stosują to podejście, ponieważ chcą być instalowane we współużytkowanym systemie hostingu i nie są zależne od usługi systemu Windows, która musi zostać zainstalowana. Zwykle uruchamiają długo działający wątek w Global.asax podczas uruchamiania aplikacji i powodują, że ten wątek przetwarza zadania w kolejce.

Oprócz ograniczenia zasobów dostępnych dla usług IIS / ASP.NET w celu przetwarzania żądań, występują również problemy z zabijaniem wątku, gdy domena AppDomain jest odtwarzana, a następnie musisz radzić sobie z trwałością zadania podczas lotu, ponieważ a także rozpoczęcie pracy z powrotem po przywróceniu AppDomain.

Należy pamiętać, że w wielu przypadkach AppDomain jest automatycznie odtwarzana w domyślnych odstępach czasu, a także po zaktualizowaniu pliku web.config itp.

Jeśli w dowolnym momencie możesz poradzić sobie z trwałością i transakcyjnymi aspektami zabicia wątku, możesz obejść recykling AppDomain, korzystając z zewnętrznego procesu, który wysyła żądanie w Twojej witrynie w określonych odstępach czasu - tak, że jeśli witryna zostanie poddana recyklingowi, gwarantują automatyczne ponowne uruchomienie kopii zapasowej w ciągu X minut.

Ponownie, jest to zazwyczaj zły pomysł.

EDYCJA: Oto kilka przykładów tej techniki w akcji:

Community Server: korzystanie z usług systemu Windows w porównaniu z wątkiem w tle do uruchamiania kodu w zaplanowanych odstępach czasu Tworzenie wątku w tle przy pierwszym uruchomieniu witryny

EDYCJA (z odległej przyszłości) - Obecnie używałbym Hangfire .

Bryan Batchelder
źródło
1
Dzięki, to wnikliwe. Recykling jest zdecydowanym zabójcą i wyciągam z tego, co mówisz, że mogę to zrobić jako rzecz twardą i szybką, ale jest to niebezpieczne.
Frans
Tak, sprawdź to: professionalaspnet.com/archive/2007/09/02/ ... A oto wątek o tym, jak serwer społecznościowy radzi sobie z tym samym: dev.communityserver.com/forums/t/459942.aspx
Bryan Batchelder
W moim przypadku mogłem po prostu powiedzieć usługom IIS, aby nigdy nie poddawały recyklingowi i nigdy nie były bezczynne w witrynie. To sprawiło, że mogłem pracować przy tym założeniu i że witryna zostanie poddana recyklingowi tylko podczas wdrażania i za każdym razem, gdy zajdzie planowana konserwacja. Ponieważ robiłem tylko witrynę typu administracyjnego, wyszło to idealnie dla mnie.
Brett
1
Dziwne, że ma to tak wiele pozytywnych głosów. To, że jest to możliwe, jest jedną z bardzo fajnych rzeczy w ASP.NET: wszystkie żądania są obsługiwane w tej samej domenie AppDomain razem z wszelkimi wątkami w tle, które możesz utworzyć. Żaden z podanych powodów, dla których jest to „zły pomysł”, nie wydaje mi się przekonujący. Domeny aplikacji mogą przestać działać, tak - ale nie jest to unikalne dla środowiska ASP.NET. Musisz dobrze bawić się swoim środowiskiem hostingowym (Google dla HostingEnvironment.RegisterObject).
John,
3
.NET 4.5.2 dodał nową funkcję QueueBackgroundWorkItemdo łatwego rejestrowania zadań w tle, możesz ponownie zaktualizować swoją odpowiedź.
Scott Chamberlain
39

Nie zgadzam się z zaakceptowaną odpowiedzią.

Korzystanie z wątku w tle (lub zadania uruchomionego za pomocą Task.Factory.StartNew) jest w porządku w ASP.NET. Podobnie jak w przypadku wszystkich środowisk hostingowych, możesz chcieć zrozumieć i współpracować z obiektami zarządzającymi zamykaniem.

W ASP.NET można zarejestrować pracę wymagającą bezpiecznego zatrzymania podczas zamykania przy użyciu HostingEnvironment.RegisterObjectmetody. Zobacz ten artykuł i komentarze do dyskusji.

(Jak zauważył Gerard w swoim komentarzu, istnieje teraz również HostingEnvironment.QueueBackgroundWorkItemwezwanie RegisterObjectdo zarejestrowania harmonogramu dla elementu w tle do pracy. Ogólnie nowa metoda jest ładniejsza, ponieważ jest oparta na zadaniach).

Jeśli chodzi o ogólny temat, o którym często słyszysz, że jest to zły pomysł, rozważ alternatywę polegającą na wdrożeniu usługi systemu Windows (lub innego rodzaju aplikacji pozaprocesowej):

  • Koniec z trywialnym wdrażaniem dzięki wdrożeniu internetowemu
  • Nie można wdrożyć wyłącznie w witrynach sieci Web platformy Azure
  • W zależności od charakteru zadania w tle procesy będą prawdopodobnie musiały się komunikować. Oznacza to, że albo jakaś forma niezależnego konsultanta lub usługa będą musiały mieć dostęp do wspólnej bazy danych.

Należy również zauważyć, że niektóre zaawansowane scenariusze mogą nawet wymagać, aby wątek w tle działał w tej samej przestrzeni adresowej, co żądania. Fakt, że ASP.NET może to zrobić, uważam za wielką zaletę, która stała się możliwa dzięki .NET.

Jan
źródło
To jest świetne. Mam zadanie, które trwa około 30 sekund. Idealne, jeśli potrzebujesz tylko opóźnić pulę aplikacji wystarczająco długo, aby wykonać pracę.
Scuba Steve
1
Z .Net Framework 4.5.2 można również użyć HostingEnvironment.QueueBackgroundWorkItem (Action <CancellationToken>)
Gerard Carbó
Z moich obserwacji wynika, że ​​IIS ostatecznie wyłącza uruchamiany przeze mnie interfejs ASP.NET Web API i nie uruchamia go ponownie, dopóki nie nadejdzie nowe żądanie. Tak więc w wyznaczonym czasie, kiedy mój wątek powinien uruchomić aplikację, może nawet nie biegać. Czy się mylę co do tego?
David Sopko,
7

Nie chcesz używać wątku z puli wątków usług IIS do tego zadania, ponieważ spowoduje to, że ten wątek nie będzie mógł przetwarzać przyszłych żądań. Możesz zajrzeć do stron asynchronicznych w ASP.NET 2.0 , ale to też nie byłaby właściwa odpowiedź. Zamiast tego wydaje się, że warto byłoby przyjrzeć się usłudze Microsoft Message Queuing . Zasadniczo należy dodać szczegóły zadania do kolejki i inny proces w tle (prawdopodobnie usługa systemu Windows) byłby odpowiedzialny za wykonanie tego zadania. Najważniejsze jest jednak to, że proces w tle jest całkowicie odizolowany od usług IIS.

senfo
źródło
Ach, MSMQ - jedna z moich ulubionych technologii od dłuższego czasu. Dzięki za wgląd i link.
Frans
6

Sugerowałbym użycie HangFire do takich wymagań. Jest to ładny silnik typu „fire and zapomnij”, który działa w tle, obsługuje inną architekturę, jest niezawodny, ponieważ jest wspierany przez pamięć trwałą.

Vipul
źródło
5

Tutaj jest dobry wątek i przykładowy kod: http://forums.asp.net/t/1534903.aspx?PageIndex=2

Bawiłem się nawet pomysłem wywołania z wątku strony utrzymującej życie w mojej witrynie, aby pomóc utrzymać pulę aplikacji przy życiu. Pamiętaj, że jeśli używasz tej metody, potrzebujesz naprawdę dobrej obsługi odzyskiwania, ponieważ aplikacja może w dowolnym momencie zostać ponownie użyta. Jak wielu wspomniało, nie jest to właściwe podejście, jeśli masz dostęp do innych opcji usług, ale w przypadku hostingu współdzielonego może to być jedyna opcja.

Aby pomóc w utrzymaniu puli aplikacji przy życiu, możesz wysłać żądanie do własnej witryny podczas przetwarzania wątku. Może to pomóc w utrzymaniu puli aplikacji przy życiu, jeśli proces trwa długo.

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }
Daniel
źródło
5

Zaczęliśmy tą ścieżkę i faktycznie działało dobrze, gdy nasza aplikacja była na jednym serwerze. Kiedy chcieliśmy skalować w poziomie do wielu komputerów (lub używać wielu w3wp w sieci Web Garen), musieliśmy ponownie ocenić i przyjrzeć się, jak zarządzać kolejką roboczą, obsługą błędów, ponownymi próbami i trudnym problemem prawidłowego blokowania, aby zapewnić tylko jedną serwer przejmuje następny element.

... zdaliśmy sobie sprawę, że nie zajmujemy się pisaniem silników przetwarzania w tle, więc szukaliśmy istniejących rozwiązań i wylądowaliśmy przy użyciu niesamowitego projektu OSS Hangfire

Sergey Odinokov stworzył prawdziwy klejnot, od którego naprawdę łatwo zacząć, i który pozwala na zmianę zaplecza, w jaki sposób praca jest utrwalana i umieszczana w kolejce. Hangfire używa wątków w tle, ale utrzymuje zadania, obsługuje ponowienia i zapewnia wgląd w kolejkę roboczą. Tak więc prace związane z ogniem hangarowym są solidne i przetrwają wszystkie kaprysy związane z recyklingiem aplikacji itp.

Jego podstawowa konfiguracja wykorzystuje serwer sql jako magazyn, ale można zamienić go na Redis lub MSMQ, gdy nadejdzie czas na skalowanie w górę. Posiada również doskonały interfejs użytkownika do wizualizacji wszystkich zadań i ich statusu, a także umożliwia ponowne kolejkowanie zadań.

Chodzi mi o to, że chociaż w wątku w tle można robić to, co chcesz, to jest dużo pracy, aby uczynić go skalowalnym i solidnym. Jest to dobre dla prostych obciążeń, ale kiedy sprawy stają się bardziej złożone, wolę używać specjalnie zbudowanej biblioteki, zamiast wykonywać ten wysiłek.

Aby uzyskać więcej informacji na temat dostępnych opcji, zapoznaj się z blogiem Scotta Hanselmana, w którym opisano kilka opcji obsługi zadań w tle w asp.net. (Dał hangfire świetną recenzję)

Jak wspomina John, warto przeczytać blog Phila Haacka o tym, dlaczego to podejście jest problematyczne i jak wdzięcznie przerwać pracę nad wątkiem, gdy domena aplikacji jest wyładowana.

Avner
źródło
2

Czy możesz utworzyć usługę systemu Windows, aby wykonać to zadanie? Następnie użyj zdalnej usługi .NET z serwera sieci Web, aby wywołać usługę systemu Windows w celu wykonania akcji? Jeśli tak jest, to właśnie bym zrobił.

To wyeliminowałoby potrzebę korzystania z usług IIS i ograniczyłoby część jego mocy obliczeniowej.

Jeśli nie, zmusiłbym użytkownika, aby siedział tam, gdy proces jest zakończony. W ten sposób upewnisz się, że został on ukończony i nie został zabity przez usługi IIS.

David Basarab
źródło
2

Wydaje się, że istnieje jeden obsługiwany sposób hostowania długotrwałych prac w usługach IIS. Wydaje się, że usługi przepływu pracy zostały zaprojektowane do tego celu, szczególnie w połączeniu z oprogramowaniem Windows Server AppFabric . Projekt pozwala na ponowne wykorzystanie puli aplikacji, wspierając automatyczne utrzymywanie i wznawianie długotrwałej pracy.

Olly
źródło
2

Możesz uruchamiać zadania w tle i zostaną ukończone nawet po zakończeniu żądania. Nie pozwól, aby nieprzechwycony wyjątek został wyrzucony . Zwykle chcesz zawsze zgłaszać swoje wyjątki. Jeśli wyjątek zostanie zgłoszony w nowym wątku, spowoduje to awarię procesu roboczego usług IIS - w3wp.exe , ponieważ nie znajdujesz się już w kontekście żądania. To również spowoduje zabicie wszelkich innych uruchomionych zadań w tle, oprócz sesji wspieranych pamięcią w procesie, jeśli ich używasz. Byłoby to trudne do zdiagnozowania, dlatego praktyka jest odradzana.

Timothy Gonzalez
źródło
1

Po prostu utwórz zastępczy proces, aby uruchomić zadania asynchroniczne; nie musi to być usługa systemu Windows (chociaż w większości przypadków jest to bardziej optymalne podejście. MSMQ to zdecydowanie za dużo.

Matt Davison
źródło