web-dev-qa-db-ger.com

Kann ich Threads verwenden, um Jobs mit langer Laufzeit in IIS auszuführen?

In einer ASP.Net-Anwendung klickt der Benutzer auf eine Schaltfläche auf der Webseite. Auf diese Weise wird ein Objekt auf dem Server über die Ereignisbehandlungsroutine instanziiert und eine Methode für das Objekt aufgerufen. Die Methode geht an ein externes System, um Dinge zu erledigen, und dies kann eine Weile dauern. Ich möchte also diesen Methodenaufruf in einem anderen Thread ausführen, damit ich die Steuerung mit "Ihre Anfrage wurde übermittelt" an den Benutzer zurückgeben kann. Ich bin einigermaßen glücklich, dies als Feuer-und-Vergessen zu tun, obwohl es noch schöner wäre, wenn der Benutzer das Objekt weiterhin nach dem Status abfragen könnte.

Ich weiß nicht, ob IIS zulässt, dass mein Thread weiterläuft, auch wenn die Benutzersitzung abläuft. Stellen Sie sich vor, der Benutzer löst das Ereignis aus, und wir instanziieren das Objekt auf dem Server und lösen die Methode in einem neuen Thread aus. Der Benutzer ist mit der Meldung "Ihre Anfrage wurde übermittelt" zufrieden und schließt seinen Browser. In dieser Benutzersitzung tritt in IIS möglicherweise eine Zeitüberschreitung auf, der Thread wird jedoch möglicherweise noch ausgeführt und führt die Arbeit aus. Wird IIS zulassen, dass der Thread weiterläuft, oder wird er ihn abbrechen und das Objekt entsorgen, sobald die Benutzersitzung abgelaufen ist?

BEARBEITEN: Aus den Antworten und Kommentaren geht hervor, dass der beste Weg, dies zu tun, darin besteht, die lang andauernde Verarbeitung außerhalb von IIS zu verlagern. Hier geht es neben allem anderen um das Problem des Domain-Recyclings. In der Praxis muss ich Version 1 in begrenzter Zeit in Betrieb nehmen und muss innerhalb eines vorhandenen Frameworks arbeiten. Daher möchte ich die Service-Schicht vermeiden und daher den Wunsch haben, den Thread innerhalb von IIS einfach loszulassen. In der Praxis beträgt die "lange Laufzeit" hier nur einige Minuten, und die Nebenläufigkeit auf der Website ist gering, sodass dies in Ordnung sein sollte. Die nächste Version muss jedoch definitiv in eine separate Service-Schicht aufgeteilt werden.

81
Frans

Sie können erreichen, was Sie wollen, aber es ist in der Regel eine schlechte Idee. Mehrere ASP.NET-Blog- und CMS-Engines verfolgen diesen Ansatz, da sie auf einem gemeinsam genutzten Hostsystem installiert werden sollen und nicht von einem zu installierenden Windows-Dienst abhängig sein sollen. In der Regel wird beim Start der App ein langer Thread in Global.asax gestartet und dieser Thread-Prozess hat Aufgaben in die Warteschlange gestellt.

Neben der Reduzierung der für IIS/ASP.NET verfügbaren Ressourcen für die Verarbeitung von Anforderungen treten auch Probleme mit dem Thread auf, der beim Recycling der AppDomain beendet wird. Anschließend müssen Sie sich mit der Persistenz der Aufgabe während des laufenden Vorgangs befassen Sie können die Arbeit auch wieder aufnehmen, wenn die AppDomain wieder hochgefahren wird.

Denken Sie daran, dass in vielen Fällen die AppDomain automatisch in einem Standardintervall wiederverwendet wird, sowie wenn Sie die web.config usw. aktualisieren.

Wenn Sie die Persistenz und die Transaktionsaspekte Ihres Threads, der gerade beendet wird, jederzeit in den Griff bekommen, können Sie das AppDomain-Recycling umgehen, indem Sie einen externen Prozess ausführen lassen, der in bestimmten Abständen eine Anforderung an Ihre Site sendet Es wird garantiert innerhalb von X Minuten automatisch wieder hochgefahren.

Auch dies ist normalerweise eine schlechte Idee.

EDIT: Hier sind einige Beispiele für diese Technik in Aktion:

Community Server: Verwenden von Windows-Diensten im Vergleich zum Hintergrundthread zum Ausführen von Code in festgelegten IntervallenErstellen eines Hintergrundthreads beim ersten Start der Website

EDIT (aus der fernen Zukunft) - In diesen Tagen würde ich Hangfire verwenden.

63

Ich bin mit der akzeptierten Antwort nicht einverstanden.

Die Verwendung eines Hintergrundthreads (oder einer Aufgabe, die mit Task.Factory.StartNew Gestartet wurde) ist in ASP.NET in Ordnung. Wie in allen Hosting-Umgebungen möchten Sie möglicherweise die Funktionen zum Herunterfahren verstehen und mit ihnen zusammenarbeiten.

In ASP.NET können Sie mithilfe der Methode HostingEnvironment.RegisterObject Die Arbeit registrieren, die beim Herunterfahren ordnungsgemäß beendet werden muss. Siehe dieser Artikel und die Kommentare für eine Diskussion.

(Wie Gerard in seinem Kommentar ausführt, gibt es jetzt auch HostingEnvironment.QueueBackgroundWorkItem, Das RegisterObject aufruft, um einen Scheduler für das Hintergrundelement zu registrieren, an dem gearbeitet werden soll. Insgesamt ist die neue Methode besser, da sie aufgabenorientiert ist. basierend.)

Ziehen Sie für das allgemeine Thema, von dem Sie oft hören, dass es eine schlechte Idee ist, die Alternative in Betracht, einen Windows-Dienst (oder eine andere Art von Zusatzprozessanwendung) bereitzustellen:

  • Keine triviale Bereitstellung mehr mit Web Deploy
  • Nicht ausschließlich auf Azure-Websites bereitstellbar
  • Abhängig von der Art der Hintergrundaufgabe müssen die Prozesse wahrscheinlich kommunizieren. Das bedeutet, dass entweder eine Form von IPC oder der Dienst auf eine gemeinsame Datenbank zugreifen muss.

Beachten Sie auch, dass in einigen erweiterten Szenarien der Hintergrund-Thread möglicherweise im selben Adressraum wie die Anforderungen ausgeführt werden muss. Ich sehe die Tatsache, dass ASP.NET dies kann, als einen großen Vorteil, der durch .NET möglich geworden ist.

34
John

Sie möchten keinen Thread aus dem IIS Threadpool für diese Aufgabe verwenden, da dieser Thread künftige Anforderungen nicht verarbeiten kann. Sie könnten sich mit Asynchrone Seiten in ASP befassen .NET 2. , aber das wäre auch nicht die richtige Antwort. Stattdessen würden Sie davon profitieren, wenn Sie sich mit Microsoft Message Queuing befassen Taskdetails in der Warteschlange und ein anderer Hintergrundprozess (möglicherweise ein Windows-Dienst) sind für die Ausführung dieser Aufgabe zuständig, aber das Endergebnis ist, dass der Hintergrundprozess vollständig von IIS isoliert ist.

7
senfo

Ich würde vorschlagen, HangFire für solche Anforderungen zu verwenden. Es ist ein Nice fire and forget Motor, der im Hintergrund läuft, verschiedene Architekturen unterstützt und zuverlässig ist, da er durch Persistenzspeicherung gesichert ist.

6
Vipul

Hier gibt es einen guten Thread und Beispielcode: http://forums.asp.net/t/1534903.aspx?PageIndex=2

Ich habe sogar mit der Idee gespielt, aus dem Thread heraus eine Keep-Alive-Seite auf meiner Website aufzurufen, um den App-Pool am Leben zu erhalten. Denken Sie bei dieser Methode daran, dass Sie eine wirklich gute Wiederherstellung benötigen, da die Anwendung jederzeit wiederverwendet werden kann. Wie viele bereits erwähnt haben, ist dies nicht der richtige Ansatz, wenn Sie Zugriff auf andere Serviceoptionen haben. Für Shared Hosting ist dies jedoch möglicherweise eine Ihrer einzigen Optionen.

Um den App-Pool am Leben zu erhalten, können Sie während der Verarbeitung des Threads eine Anforderung an Ihre eigene Site senden. Dies kann dazu beitragen, den App-Pool am Leben zu erhalten, wenn Ihr Prozess längere Zeit ausgeführt wird.

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;
    }
5
Daniel

Wir haben diesen Weg eingeschlagen und es hat tatsächlich funktioniert, als sich unsere App auf einem Server befand. Wenn wir auf mehrere Maschinen skalieren wollten (oder mehrere w3wp in einer Web-Garen verwenden wollten), mussten wir die Verwaltung einer Arbeitswarteschlange, die Fehlerbehandlung, Wiederholungsversuche und das schwierige Problem des korrekten Sperrens überprüfen, um nur eine zu gewährleisten Server holt das nächste Objekt ab.

... wir haben festgestellt, dass wir keine Engines für die Hintergrundverarbeitung schreiben, also haben wir nach vorhandenen Lösungen gesucht und sind mit dem großartigen OSS-Projekt gelandet Hangfire

Sergey Odinokov hat ein echtes Juwel geschaffen, mit dem man sehr einfach anfangen kann und mit dem man das Backend austauschen kann, wie die Arbeit fortgeführt und in die Warteschlange gestellt wird. Hangfire verwendet Hintergrund-Threads, behält jedoch die Jobs bei, verarbeitet Wiederholungen und gibt Ihnen Einblick in die Arbeitswarteschlange. So sind Hangfire-Jobs robust und überstehen alle Unregelmäßigkeiten von recycelten Appdomains usw.

In der Grundkonfiguration wird SQL Server als Speicher verwendet. Sie können jedoch zum Zeitpunkt der Skalierung gegen Redis oder MSMQ austauschen. Es verfügt auch über eine hervorragende Benutzeroberfläche zur Visualisierung aller Jobs und ihres Status sowie zum erneuten Einreihen von Jobs in die Warteschlange.

Mein Punkt ist, dass es zwar durchaus möglich ist, in einem Hintergrund-Thread das zu tun, was Sie wollen, aber viel Arbeit erforderlich ist, um ihn skalierbar und robust zu machen. Es ist in Ordnung für einfache Workloads, aber wenn die Dinge komplexer werden, bevorzuge ich die Verwendung einer zweckgebundenen Bibliothek, anstatt diese Anstrengungen zu unternehmen.

Weitere Informationen zu den verfügbaren Optionen finden Sie in Scott Hanselmans Blog , in dem einige Optionen für die Verarbeitung von Hintergrundjobs in asp.net behandelt werden. (Er gab Hangfire eine glühende Kritik)

Wie auch von John erwähnt, lohnt es sich, in Phil Haacks Blog nachzulesen, warum der Ansatz problematisch ist und wie man die Arbeit am Thread ordnungsgemäß beendet, wenn die Appdomain entladen wird.

4
Avner

Sie können Aufgaben im Hintergrund ausführen, die auch nach dem Ende der Anforderung ausgeführt werden. Lass nicht zu, dass eine nicht abgefangene Ausnahme geworfen wird . Normalerweise möchten Sie immer Ihre Ausnahmen werfen. Wenn eine Ausnahme in einem neuen Thread ausgelöst wird, stürzt der IIS-Arbeitsprozess - w3wp.exe ab, da Sie sich nicht mehr im Kontext der Anforderung befinden. Dadurch werden auch alle anderen Hintergrundaufgaben beendet, die Sie neben durch den Arbeitsspeicher gesicherten Sitzungen ausführen, sofern Sie diese verwenden. Dies ist schwer zu diagnostizieren, weshalb von der Praxis abgeraten wird.

2

Können Sie einen Windows-Dienst für diese Aufgabe erstellen? Rufen Sie dann mit .NET Remoting vom Webserver den Windows-Dienst auf, um die Aktion auszuführen. Wenn das der Fall ist, würde ich das tun.

Dies würde die Notwendigkeit der Weiterleitung auf IIS beseitigen und einen Teil der Verarbeitungsleistung einschränken.

Wenn nicht, würde ich den Benutzer zwingen, dort zu sitzen, während der Prozess abgeschlossen ist. Auf diese Weise stellen Sie sicher, dass es vollständig ist und nicht von IIS beendet wird.

2
David Basarab

Es scheint einen unterstützten Weg zu geben, um langjährige Arbeit in IIS zu hosten. Workflow Services scheinen dafür konzipiert zu sein, insbesondere in Verbindung mit Windows Server AppFabric . Das Design ermöglicht das Recycling des Anwendungspools, indem es die automatische Beibehaltung und Wiederaufnahme der langjährigen Arbeit unterstützt.

2
Olly

Erstellen Sie einfach einen Ersatzprozess, um die asynchronen Aufgaben auszuführen. Es muss kein Windows-Dienst sein (obwohl dies in den meisten Fällen der optimalere Ansatz ist. MSMQ ist weit über Kill.

1
Matt Davison