Hintergrundaufgaben auf einer großen Site ausführen


49

Wir haben es mit einem interessanten Problem bei StackOverflow zu tun.

Wir haben eine ganze Reihe kleiner Aufgaben, die bald erledigt werden müssen. Ein Beispiel ist das Aktualisieren von Listen mit verwandten Fragen. In der Vergangenheit haben wir diese Aufgaben auf die Seitenladevorgänge einiger Benutzer übertragen.

Das war nie ideal, aber es war nicht wirklich auffällig. Jetzt, da SO das 1.000.000-Fragezeichen überschritten hat, spüren es die unglücklichen Benutzer.

Die natürliche Lösung besteht darin, diese Aufgaben tatsächlich in den Hintergrund zu rücken. Ich habe zwei Möglichkeiten, dies zu tun.

1. In IIS als benutzerdefinierter Thread-Pool / Work-Queue

Grundsätzlich drehen wir einige Threads (nicht ThreadPool , um IIS nicht zu beeinträchtigen) und lassen sie einige Sammlungen bedienen, in die wir Funcs schieben .

Der große Vorteil hier ist die Einfachheit. Wir müssen uns nicht darum kümmern, etwas zu organisieren, und wir müssen auch nicht sicherstellen, dass ein externer Dienst verfügbar ist und reagiert.

Wir erhalten auch Zugriff auf alle unsere gemeinsamen Codes.

Der Nachteil ist, dass wir keine Hintergrund-Threads verwenden sollten. Die Einwände, die ich kenne, beziehen sich alle auf das Verhungern von IIS (wenn Sie ThreadPool verwenden) und auf das zufällige Absterben der Threads (aufgrund des AppPool-Recyclings).

Wir haben eine vorhandene Infrastruktur, um den zufälligen Thread-Tod zu einem Nicht-Problem zu machen (es ist im Grunde genommen möglich, einen abgebrochenen Task zu erkennen), und die Anzahl der Threads zu begrenzen (und Threads zu verwenden, die keine ThreadPool-Threads sind) ist ebenfalls nicht schwierig.

Vermisse ich irgendwelche anderen Einwände in den IIS-Prozessthreadpools / Arbeitswarteschlangen?

In StackOverflow verschoben , da es hier nicht wirklich angesprochen wurde.

2. Als Dienstleistung

Entweder eine Lösung von Drittanbietern oder eine benutzerdefinierte.

Grundsätzlich würden wir eine Aufgabe über die Prozessgrenzen hinweg auf einen Dienst verlagern und dies einfach vergessen. Vermutlich verknüpfen wir Code oder beschränken uns auf unformatiertes SQL + eine Verbindungszeichenfolge.

Der Profi ist, dass es der "richtige Weg" ist, dies zu tun.

Die Nachteile sind, dass wir entweder sehr eingeschränkte Möglichkeiten haben oder ein System ausarbeiten müssen, um diesen Service mit unserer Codebasis synchron zu halten. Wir müssen auch alle unsere Überwachungs- und Fehlerprotokollierungen irgendwie einbinden, die wir mit der Option "In IIS" kostenlos erhalten.

Gibt es weitere Vorteile oder Probleme mit dem Serviceansatz?

Kurz gesagt, gibt es unvorhergesehene und unüberwindbare Probleme, die Ansatz 1 unbrauchbar machen, und wenn ja, gibt es gute Dienste von Drittanbietern, die wir nach Ansatz 2 suchen sollten?


Der richtige Weg ist der, dass Sie, wenn Sie sich entscheiden, den anderen Weg einzuschlagen, zurückblicken und sagen, wir hätten das richtig machen sollen. Mit Bedacht wählen. Ich bin mit der IIS-Welt nicht vertraut genug, um dieses spezielle Problem zu kommentieren.
Chris

2
Ich bin neugierig, weil ich ein ähnliches Szenario (in einem viel kleineren Maßstab) habe und auch einige zufällige Benutzer, die keine glückliche Verbindung haben, nur huckepack nehmen. Ich kenne die beste Lösung nicht und folge ihr hier. :-)
pc1oad1etter

7
Ich verstehe nicht, warum dies nicht auf StackOverflow ist. Dies ist ein technischer Kompromiss, keine subjektive Bewertung. Sie fordern eine Analyse der verschiedenen Ansätze - das ist alles objektiv. Erst wenn die Analyse klargestellt hat, um welche Kompromisse es sich genau handelt, gibt es eine Subjektivität, und so weit ich sehen kann, lautet Ihre Frage nicht: Was sollte ich wichtiger finden, meine Zeit und Serverressourcen oder die Zeit meines Benutzers? ' oder etwas ähnliches.
Joren

@ Kevin Montrose - aus Ihren Kommentaren geht hervor, dass Sie zwischen "muss bald erledigt werden" und "nach Zeitplan" unterscheiden. Können Sie erläutern, warum dies zwei verschiedene Arten von Hintergrundaufgaben sind, die ein anderes Muster / eine andere Infrastruktur erfordern?
Portman

@Portman - Der grundlegende Unterschied besteht darin, dass "baldige" Aufgaben nicht spekulativ erledigt werden können. Wir müssen wirklich warten, bis wir wissen, dass sie erledigt werden müssen. Einige der Umschlagberechnungen zeigen, dass, wenn wir "Verwandte Fragen" -Abfragen (nur eine von vielen) auf einen "dummen" Cron-Tab verschieben würden, dies ca. 30 Sekunden dauern würde. eine Woche solider Ausführung, um alle Fragen zu klären. Im Allgemeinen möchten wir auch, dass sie so schnell wie möglich ausgeführt werden (ohne die Benutzererfahrung zu beeinträchtigen), während unsere Intervallaufgaben nur einmal in 5 Minuten ausgeführt werden (und normalerweise viel seltener).
Kevin Montrose

Antworten:


17

Vor ein paar Wochen habe ich eine ähnliche Frage zu SO gestellt. Kurz gesagt, besteht mein Ansatz seit einiger Zeit darin, einen Windows-Dienst zu entwickeln. Ich würde NServiceBus (im Wesentlichen MSMQ unter dem Deckmantel) verwenden, um Anforderungen von meiner Web-App an meinen Dienst zu senden. Früher habe ich WCF verwendet, aber eine verteilte Transaktion über WCF richtig funktionieren zu lassen, schien mir immer ein Nervenkitzel zu sein. NServiceBus hat den Trick gemacht, ich konnte Daten festschreiben und Aufgaben in einer Transaktion erstellen und mir keine Sorgen machen, ob mein Dienst zu diesem Zeitpunkt aktiv war. Als einfaches Beispiel: Wenn ich jemals eine E-Mail senden müsste (zum Beispiel eine Registrierungs-E-Mail), würde ich das Benutzerkonto erstellen und in einer Transaktion ein Signal an meinen Windows-Dienst auslösen (um die E-Mail zu senden). Der Message-Handler auf der Serviceseite würde die Nachricht abholen und entsprechend verarbeiten.

Seit der Veröffentlichung von ASP .NET 4.0 und AppFabric gibt es eine Reihe praktikabler Alternativen zu dem oben genannten Mechanismus. Unter Bezugnahme auf die oben erwähnte Frage haben wir jetzt AppFabric's AppInitialize (via net.pipe) sowie die Auto-Start-Funktion von ASP .NET 4.0, die die Entwicklung von Windows-Diensten als Web-Apps zu einer praktikablen Alternative macht. Ich habe jetzt aus mehreren Gründen damit begonnen (der größte Grund ist, dass der Einsatz nicht mehr nervt):

  1. Sie können über Ihren Service eine Web-Benutzeroberfläche entwickeln (da diese als Web-App ausgeführt wird). Dies ist äußerst nützlich, um zu sehen, was zur Laufzeit passiert.
  2. Ihr Bereitstellungsmodell für Ihre Webanwendungen funktioniert für Ihre Dienstanwendung.
  3. IIS bietet einige nützliche Funktionen für die Behandlung von Anwendungsfehlern (in mancher Hinsicht ähnlich wie bei einem Windows-Dienst).
  4. Webentwickler sind (natürlich) mit der Entwicklung von Webanwendungen bestens vertraut. Die meisten wissen nicht viel über bewährte Methoden bei der Entwicklung eines Windows-Dienstes.
  5. Es bietet eine Reihe von Alternativen zum Offenlegen einer API für andere Apps.

Wenn Sie diesen Weg gehen (verzeihen Sie mir das Kopieren und Einfügen von meinem ursprünglichen Beitrag), würde ich auf jeden Fall in Betracht ziehen, die Hintergrundlogik in einer separaten Webanwendung auszuführen. Dafür gibt es mehrere Gründe:

  1. Sicherheit . Möglicherweise gibt es ein anderes Sicherheitsmodell für die Benutzeroberfläche, in dem Informationen zu den ausgeführten Hintergrundprozessen angezeigt werden. Ich möchte diese Benutzeroberfläche nur dem Ops-Team zur Verfügung stellen. Die Webanwendung wird möglicherweise auch als ein anderer Benutzer ausgeführt, der über erhöhte Berechtigungen verfügt.
  2. Wartung . Es wäre großartig, Änderungen an der Anwendung, die die Hintergrundprozesse hostet, bereitstellen zu können, ohne die Verwendung der Front-End-Website durch den Benutzer zu beeinträchtigen.
  3. Leistung . Wenn die Anwendung von der Hauptwebsite getrennt ist, in der Benutzeranforderungen verarbeitet werden, bedeutet dies, dass Hintergrundthreads die Fähigkeit von IIS zur Verarbeitung der Warteschlange für eingehende Anforderungen nicht beeinträchtigen. Darüber hinaus kann die Anwendung, die die Hintergrundaufgaben verarbeitet, bei Bedarf auf einem separaten Server bereitgestellt werden.

Dadurch kehren wir zum Marshalling-Aspekt zurück. WCF, NServiceBus / RabbitMQ / ActiveMQ usw., Vanille MSMQ, RESTful API (Think MVC) sind alle Optionen. Wenn Sie Windows Workflow 4.0 verwenden, können Sie einen Hostendpunkt bereitstellen, den Ihre Webanwendung verwenden kann.

Der Webhosting-Ansatz für Services ist für mich noch ziemlich neu, nur die Zeit wird zeigen, ob es die richtige Wahl war. Soweit so gut. Übrigens, wenn Sie AppFabric nicht verwenden möchten (ich konnte es aus irgendeinem bizarren Grund nicht, weil Windows Server Web Edition nicht unterstützt wird), funktioniert die im Beitrag des Gu erwähnte Autostart-Funktion einwandfrei. Halten Sie sich jedoch von der Datei applicationhost.config fern. Alles in diesem Beitrag kann über die IIS-Konsole (Konfigurationseditor auf der Hauptserverebene) eingerichtet werden.

Hinweis: Ich hatte ursprünglich ein paar weitere Links in dieser Nachricht gepostet, aber leider ist dies mein erster Beitrag zu diesem Austausch und es wird nur ein Link unterstützt! Grundsätzlich gab es zwei andere, um ihnen Google "Tod an Windows-Dienste ... Es lebe AppFabric!" und "Auto-Start-Asp-Net-Anwendungen". Das tut mir leid.


Die Grundidee, eine separate Website als Service zu verwenden, ist eine faszinierende, die ich nicht in Betracht gezogen habe ...
Kevin Montrose

Rohland, hier fehlt vielleicht etwas, aber Sie scheinen zu sagen, dass Sie über Ihren NServiceBus-Handler mit einem Windows-Dienst interagiert haben. Der Dienst sendet dann die E-Mail. Wenn ich recht habe, kann ich fragen, warum Sie die E-Mail nicht von einem NServiceBus-Message-Handler senden, der sehr einfach zu entwickeln, zu testen und bereitzustellen wäre?
Sean Kearon

Die Website sendet eine Nachricht an den Windows-Dienst. Der Windows-Dienst-NServiceBus-Nachrichtenhandler nimmt die Nachricht auf und sendet sie. Dies ist im Wesentlichen derselbe Prozess, den Sie beschreiben.
Rohland

22

Tatsächlich gibt es in Windows eine dritte Möglichkeit, Hintergrunddienste auszuführen, und diese ist in der UNIX-Welt weit verbreitet. Der dritte Weg ist ein CRONJob, der einen Teil Ihrer Infrastruktur betreibt. In Windows ist dies als das bekannt task schedulerund wird häufig für die geplante Ausführung von Code verwendet. Um dies zu verwenden, erstellen Sie eine Befehlszeilen-App, die nach einem vordefinierten Zeitplan ausgeführt wird. Dies hat den Vorteil, dass Sie sich keine Sorgen machen müssen, wenn der Prozess wie ein Dienst in Betrieb bleibt. Wenn er aus irgendeinem Grund fehlschlägt, wird er nur beim nächsten Mal gestartet.

Für das Marshalling bestimmter Tasks müssen Sie diese Tasks nur in einem permanenten Binärspeicher speichern. Bis die Befehlszeilen-App sie aus dem Speicher auswählt und ausführt. Ich habe dies in der Vergangenheit getan, indem ich die Cassandra-Datenbank als Sitzungszustandsanbieter verwendet habe, um Hintergrundaufgaben für bestimmte Benutzer in der Cassandra-Datenbank zu erledigen und sie dann von der Befehlszeile auswählen und für den Benutzer ausführen zu lassen.

Dies war vielleicht nicht die typische Marshalling-Lösung, aber für mich hat es sehr gut funktioniert, und es hat sich als sehr elegante Lösung herausgestellt, da die geplanten Aufgaben Herunterfahren, Netzwerkprobleme überstanden haben und jeder Computer die Aufgabe ausführen konnte, da sie zentral war gelagert.

Schamlose Werbung, aber das ist mein Projekt und die Lösung, die ich kurz beschrieben habe, ist, warum ich das Projekt erstellt habe: http://github.com/managedfusion/fluentcassandra/


2
Ich mache das mit meinem Shared Hosting Service, da ich keinen Shell-Zugang habe. Schreiben Sie eine PHP-Seite, die etwas Wichtiges tut, und erstellen Sie dann einen Cron-Job, der die Seite regelmäßig mit wget oder lynx lädt. Das klingt nach genau der Art von Dingen, die in diesem Fall funktionieren würden und die extrem einfach sind und kaum eine Änderung der Art und Weise erfordern, in der die Dinge derzeit ausgeführt werden.
Ricket

Was für eine einfache Lösung. Es hat Ideen für mein eigenes Projekt ausgelöst, über die ich noch nicht einmal nachgedacht habe. Außerdem haben Sie vollen Zugriff auf Ihre vorhandene Codebasis. Fügen Sie der Lösung einfach ein Konsolenprojekt hinzu und verweisen Sie auf die vorhandenen Projekte.
Tim Murphy

10

Cron + Web App

Dies ist ein kampferprobtes Design, das sich horizontal mit Ihrer Webfarm skalieren lässt und sicherstellt, dass Sie den Web-Technologie-Stack verwenden, den Sie bereits kennen.

So funktioniert das:

  1. Erstellen Sie in Ihrer Webanwendung einen Controller / eine Aktion für geplante Hintergrundaufgaben. Normalerweise rufe ich meins an http://mydomain.com/system/cron.
  2. Aus Sicherheitsgründen sollte diese Aktion auf nur authentifizierte IP-Adressen im lokalen Netzwerk beschränkt werden.
  3. Installieren Sie auf einem separaten Computer Wget und richten Sie eine geplante Aufgabe ein, damit Wget die Ressource aus Schritt 1 abruft. Sie können die Aufgabe so oft ausführen lassen, wie Sie möchten (ich wähle normalerweise 30 Sekunden). Vergessen Sie nicht, das entsprechende Cookie-Argument an Wget zu übergeben, damit es sich bei Ihrer Web-App authentifiziert.
  4. Aus Redundanzgründen können Sie auch ein zweites geplantes wget auf einem zweiten Computer installieren.

Hurra! Jetzt haben Sie eine Route, die alle 30 Sekunden aufgerufen wird. Und wenn die Verarbeitung der Anfrage 5 Minuten dauert, ist dies für niemanden von Belang, da sie nicht Teil der Seitenanfrage eines Benutzers ist.

Die cronAktion sieht sehr einfach aus: Er hat eine Liste von Methoden, die auf einer bestimmten Frequenz ausgeführt werden können. Wenn eine Anforderung eingeht, sieht er, ob eine Methode ausgeführt werden muss, und ruft die entsprechende Methode auf. Dies bedeutet, dass Sie den Zeitplan in Ihrer Datenbank steuern können , in der Sie wahrscheinlich bereits viele andere wichtige Konfigurationsdaten für Ihre Site haben.

Wichtiger noch (für Sie) ist, dass Ihre Jobs nicht nach einem festen Zeitplan abgerufen werden müssen. Sie können eine beliebige Logik schreiben, um zu bestimmen, wann eine Methode ausgeführt werden soll.

Vor-und Nachteile

Vorteile
  • Sie sind bereits sehr gut darin, ASP.NET MVC-Code zu schreiben. Auf diese Weise können Sie Ihre Hintergrundaufgaben auf derselben Plattform schreiben, auf der Sie den Rest Ihrer Lösung schreiben.
  • Die Aufgaben laufen im gleichen Kontext wie Ihre Web - App, so dass Sie den Cache teilen und nutzen Hilfsmethoden , die bereits existieren.
  • Wenn Sie einen URI mit Lastenausgleich abrufen möchten, werden jetzt auch Ihre Hintergrundaufgaben mit Lastenausgleich ausgeführt.
  • Gleichzeitige Bereitstellung - Sie müssen sich nicht um die Synchronisierung Ihrer Webanwendung mit Ihrer Hintergrundaufgabenlogik kümmern, da sich alle in derselben Bereitstellung befinden.
Nachteile
  • Im Laufe der Jahre haben mir einige Leute gesagt, dieses Design sei "hochgradig gekoppelt", aber wenn sie gedrückt wurden, konnten sie nicht artikulieren, warum das eine schlechte Sache ist.

Hinweis: Wenn Sie Fragen oder Bedenken haben, fügen Sie bitte einen Kommentar hinzu . Ich bin glücklich, näher darauf einzugehen.


7

Ich habe in meiner aktuellen Anwendung so gut wie jede Möglichkeit ausprobiert und genutzt. Ich habe damit begonnen, dasselbe zu tun, was Sie derzeit tun, nämlich eine Benutzeranforderung zu bearbeiten, um die Daten zu füllen, und sie dann für die Zukunft zwischenzuspeichern. Ich erkannte, dass dies auch eine schlechte Idee war (vor allem, da Sie auf mehrere Webserver skalieren, nehmen mehr Benutzer den Treffer).

Ich hatte auch einen geplanten Auftrag, bei dem eine URL in der ASP.NET-App aufgerufen wurde. Dies ist eine anständige Lösung, die jedoch ab dem Zeitpunkt, zu dem Sie einen Webserver überschritten haben, eine Panne aufweist.

Gegenwärtig verwende ich zwei verschiedene Methoden, beide mit Quartz.NET, einer großartigen kleinen Bibliothek. Das erste ist Quartz.NET, das mit ASP.NET in-process ausgeführt wird, das in global.asax eingerichtet wird und alle paar Minuten ausgeführt wird. Ich verwende dies, um den ASP.NET-Cache außerhalb des Bandes zu aktualisieren, was der einzige Grund ist, warum er als Teil von ASP.NET ausgeführt wird.

Das zweite ist, dass ich eine Bibliothek namens DaemonMaster geschrieben habe, um Quartz.NET zu verpacken - es macht es einfach, eine DLL in ein Verzeichnis abzulegen und in einem Windows-Dienst ausführen zu lassen. Ich fand, dass es hilft, einige der lästigen Teile der Arbeit mit einem Windows-Dienst zu vermeiden und auch die Quartz.NET-API zu bereinigen. Die Dienste, die über DaemonMaster ausgeführt werden, unterscheiden sich in zwei Varianten. Bei der ersten handelt es sich um Jobs, die jede Nacht oder alle X Minuten ausgeführt werden müssen. Die anderen Jobs arbeiten in einer Warteschlange basierend auf Daten, die von der ASP.NET-Anwendung eingehen. Die ASP.NET-App löscht JSON-Objekte in RabbitMQ, und die Dienste rufen RabbitMQ ab und verarbeiten die Daten.

Auf dieser Grundlage würde ich vorschlagen, dass Sie einen Windows-Dienst verwenden (und DaemonMaster ausprobieren) und bei Bedarf eine Warteschlange wie RabbitMQ verwenden, um die Daten von der ASP.NET-App an die Dienste zu übergeben - es hat das Beste aus all diesen Lösungen herausgeholt . Wenn Sie den Cache laden, ist die Ausführung in ASP.NET sinnvoll, ansonsten glaube ich nicht.


6

Ich würde es richtig machen und einen Windows-Dienst ausführen lassen, der eine "Warteschlange" überwacht. Ich sage "Warteschlange", weil das Programmieren mit MSMQ mit dem Einstecken heißer Poker in Ihre Augäpfel vergleichbar ist.

Ich habe mich in die Einfachheit von Delayed :: Job in Rails verliebt , und etwas Ähnliches ist in .NET problemlos möglich.

Grundsätzlich fügen Sie jede Art von SomethingOperation(etwas, das eine Perform()Methode hat). Serialisieren Sie dann einfach die relevanten Parameter, geben Sie ihr eine Priorität, eine Art Standardwiederholungsverhalten und speichern Sie sie in einer Datenbank.

Ihr Dienst überwacht dies nur und bearbeitet die Aufträge in der Warteschlange.


Das Serialisieren der relevanten Parameter ist nicht wirklich ein "nur", es ist fast das "alles". Es ist einer meiner größeren Vorbehalte gegen den separaten Prozessansatz ...
Kevin Montrose

Ja, das ist die gleiche Lösung, die ich verwendet habe, aber ich habe das gesamte Objekt als Binärdatei in die Datenbank serialisiert und sie dann zur Ausführung herausgezogen. Ich habe Cassandra als persistenten Speicher und Task Scheduler als CRON-Scheduler für die Befehlszeilen-App verwendet, die die Aufgaben ausführen und ausführen sollte.
Nick Berardi

Zunächst haben wir nur ein einfaches Datenelement in die Nachricht aufgenommen und schließlich das gesamte Objekt geworfen. Es hat immer noch super geklappt. Ich würde die Trennung in Betracht ziehen, da sie auch andere Vorteile hat.
Nathan Palmer

@ Kevin - wenn wir nur ein paar Leute mit viel Serialisierungsgeschichte hätten ...
Marc Gravell

4

Wir waren ziemlich zufrieden mit einem Service Bus / Message Queue / Service-Ansatz. Die Grundarchitektur ist dies.

Die Website sendet eine Nachricht an die Warteschlange

bus.Send(new ProjectApproved()); // returns immediately

Der Windows-Dienst empfängt und verarbeitet Nachrichten in seiner eigenen Zeit

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Do something "offline"
   }
}

Der Vorteil ist, dass der Front-End-Service, mit dem auch Benutzer verbunden sind, keine Verzögerung erfährt. Der Windows-Dienst kann heruntergefahren und aktualisiert werden, ohne dass die Hauptwebsite unterbrochen wird. Außerdem ist es extrem schnell .

Wenn Sie nicht alle Ihre Daten in der Nachricht speichern können, können Sie diese jederzeit speichern und später abrufen. Ich schlage vor, einen Dokumentenspeichermechanismus wie RavenDB oder MongoDB zu verwenden, bei dem es sehr einfach ist, Ihre Klassen ohne Änderungen zu speichern.

Die Website sendet eine Nachricht an die Warteschlange

// Save your object
store.Save(completeProject);

// Send a message indicating its ready to be processed
bus.Send(new ProjectApproved() { ProjectId = completeProject.Id });

Der Windows-Dienst empfängt und verarbeitet Nachrichten in seiner eigenen Zeit

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Retrieve your object back
      var completeProject = store.Get(Message.ProjectId);
   }
}

Zur Vereinfachung verwenden wir: Rhino ESB und Topshelf . Die Konfiguration ist äußerst einfach und die Implementierung in eine vorhandene Anwendung hat sich als sehr zeitsparend erwiesen.


Auf jeden Fall ist ein Linienbus mit CQRS mit immer ein guter Weg , um Ihre Skalierbarkeit zu verbessern
thinkbeforecoding

3

Ich bin gespannt, warum eine Kombination aus beidem nicht in Frage kommt. Im Moment lösen Sie Jobs bei Seitenaufrufen aus, und einige Pechvögel bleiben stecken und warten 10 Sekunden, bis die Seite angezeigt wird. Zumindest verstehe ich Ihre derzeitige Methode so.

Es dauert jedoch immer länger, bis diese Jobs ausgeführt werden, wenn die Site wächst, und Sie möchten die Benutzererfahrung auf der Site nicht beeinträchtigen. Nicht einmal für ein paar (oder vielleicht viele) unglückliche Benutzer im Laufe des Tages. Jetzt überlegen Sie, Jobs im Hintergrund zu planen.

Ich verstehe nicht, warum ein Hintergrundjob, der in regelmäßigen Abständen ausgeführt wird, keinen Besucher nachahmen kann. Jetzt bin ich kein Windows-Programmierer, aber in der Linux-Welt habe ich einen Cron-Job eingerichtet, der in regelmäßigen Abständen ausgeführt wird und 2 Codezeilen enthält.

#!/bin/bash
wget -O /dev/null http://stackoverflow.com/specially_crafted_url

Es vereint die Vorteile beider Systeme. Es ist im Hintergrund gemacht. Es wirkt sich nicht auf Benutzer aus. Es wird weiterhin eine Seitenansicht verwendet, um den Job zu starten. Ich habe diesen Ansatz schon einmal gesehen. Es ist in der Regel der Mittelweg zwischen den einfachen Wegen der Vergangenheit und den komplexeren Wegen, die die Straße hinunter kommen.

Aktualisieren

Ich denke, Sie können das Problem des Lastenausgleichs umgehen, indem Sie die Job-Läufer auf den Webservern selbst ausführen. Der Job-Runner zieht eine URL aus der Job-Warteschlange und führt sie folgendermaßen aus:

wget -O /dev/null http://localhost/specially_crafted_url

Aufgrund der Art der Job- / Messaging-Warteschlangen werden die Jobs gleichmäßig auf die Job-Läufer verteilt, was bedeutet, dass die speziell gestaltete URL möglicherweise auf Ihre Webserver verteilt wird.


Wir tun dies bereits für alles, was in vorhersehbaren Intervallen abläuft. Was uns noch bleibt, sind Dinge, die nicht zu weit im Voraus vorhergesagt werden können. Beispielsweise wird der "Block für verwandte Fragen" nur für Fragen aktualisiert, die kürzlich angezeigt wurden. Mit Tags versehene Fragenlisten werden ebenfalls nur zwischengespeichert, wenn jemand diese Tags überprüfen möchte. Da wir über eine Million Fragen haben und uns 25.000 Tags nähern, können wir nicht alle damit verbundenen Aufgaben ausführen (und das sind nur 2 Beispiele) "nur für den Fall".
Kevin Montrose

Es gibt auch Lastausgleichsprobleme, da SO auf mehrere Server aufgeteilt ist. Wenn Sie zu stackoverflow.com wechseln, werden Sie grundsätzlich immer auf denselben Server zugreifen. Der wget-Ansatz würde uns zwingen, alle Aufgaben auf einen einzelnen Server zu übertragen (oder unser Load-Balancing-Setup wirklich zu überarbeiten), was sehr schmerzhaft wäre.
Kevin Montrose

Seien Sie nett, wenn die Dinge in regelmäßigen Abständen ablaufen, nicht wahr? Ich verstehe, was Sie sagen, aber die oben beschriebene Methodik (und ich denke, sie wurde von einigen anderen Leuten erwähnt) ändert sich nicht. Wenn auf einer Seite die Meldung "Es ist Zeit, diesen Job auszuführen" angezeigt wird, wird der Job in eine Nachrichtenwarteschlange gestellt. Ein lang laufender Hintergrundjob führt die gefundenen Jobs aus. In diesem Fall sind die Jobs nur URLs, die angefordert werden müssen. hehe Sie könnten dies wahrscheinlich auf einem gemeinsam genutzten Server für 20 US-Dollar pro Monat einrichten, da für die Ausführung Ihre Codebasis nicht erforderlich ist. Schauen Sie sich Amazon SQS an, um einen benutzerfreundlichen Messaging-Dienst zu erhalten.
Mellowsoon

In Bezug auf Lastausgleichsprobleme. Wo ein Wille ist, ist auch ein Weg! Anstatt die Anfrage an stackoverflow.com zu richten, könnten Sie einen Server zufällig über dessen IP-Adresse treffen. Wenn der Load Balancer Cookies prüft, um Anfragen weiterzuleiten, können Sie Cookies fälschen. Wenn die IP-Adresse überprüft wird, können Sie dies möglicherweise sogar vortäuschen (da Sie sich nicht um die Antwort des Servers kümmern).
Mellowsoon

Einverstanden, dass Load Balancing kein Grund sein sollte, dies nicht zu tun. Da die Anforderung für specially_crafted_urlvon einer bekannten IP stammt, können Sie in Ihrem Load Balancer eine Regel hinzufügen, die Round-Robin-Vorgänge nur für Anforderungen von dieser IP ausführt.
Portman

2

Ich denke, der Nachteil des reinen Service-Ansatzes ist, dass Sie Code in den Service und nicht in die Kern-App verstreut haben.

Folgendes haben wir mit umfangreichen nicht zeitkritischen Hintergrundjobs gemacht, die den Code zusammenhalten und den Service vereinfachen:

  1. Erstellen einer Jobwarteschlange (entweder im Arbeitsspeicher oder in der Datenbank, unabhängig davon, welche Persistenz für die Jobtypen erforderlich ist)
  2. Erstellen Sie einen Webdienst, der die Jobs in der Warteschlange ausführt
  3. Absolut einfache Service-App, die den Web-Service in einem festgelegten Intervall aufruft. Überlassen Sie alle komplexen Aufgaben (Abrufen und Ausführen von Jobs) dem Web-Service in Ihrer Kern-Codebasis.

Noch einfacher ist es, den Anruf in einer Konsolen-App zu tätigen und ihn mit dem Taskplaner oder VisualCron in einen "Dienst" zu verwandeln.


1
Ich habe genau dies in einer wichtigen Anwendung im Einsatz - einem Windows-Dienst, der die Webanwendung in regelmäßigen Abständen auslöst. Die Web-App bleibt statusfrei und ruft den Status nach Bedarf aus der Datenbank ab. Funktioniert ein Vergnügen.
Bevan

1

Ich mochte TopShelf. Beibehaltung der Einfachheit und ordnungsgemäße Ausführung als Windows-Dienst. Erstellen Sie im Allgemeinen eine Konsolen-App, fügen Sie etwa 15 bis 20 Codezeilen hinzu, und installieren Sie sie dann als Dienst.

http://code.google.com/p/topshelf/


1

Wie wäre es mit einem sehr einfachen Windows-Dienst, der auf dem Webserver ausgeführt wird und regelmäßig eine Wartungs-URL aufruft, die Ihre verschiedenen Aufgaben erledigt. Lassen Sie es drosseln, wie viel Arbeit es in einer bestimmten Anfrage erledigt.


1

Ich werde mich hier gegen den offensichtlichen Trend wenden und vorschlagen, das In-IIS-Modell zu wählen. Ich habe es selbst benutzt und es funktioniert wirklich gut. Es ist wirklich nicht so schwer, eine anständige Thread-Pool-Klasse zu implementieren (im Laufe der Jahre habe ich meine Thread-Pool-Klasse erweitert, um die dynamische Erstellung und Zerstörung von Threads, das Wiederholen von Jobs usw. zu unterstützen). Vorteile sind:

  • Kein externer Dienst zu überwachen
  • Einfache Implementierung: Kein prozessübergreifendes Marshalling, keine erweiterte Auftragsüberwachung
  • Sie befinden sich immer noch in Ihrem IIS-Prozess, sodass Sie die gesamte gewohnte Protokollierung durchführen können usw. (mehrere Protokolldateien sind nicht erforderlich).
  • Sehr vereinfachte Bereitstellung (wenn Sie einen Dienst aktualisieren, müssen Sie den Dienst beenden, die Dateien kopieren und den Dienst starten - zusätzlich zu den üblichen Aktualisierungen des Website-Codes)

Meiner Meinung nach ist eine In-IIS-Lösung einfach der "nächste Schritt" vom Huckepack auf zufällige Seitenaufrufe.


1

Resque ist nett. Oder sogar Kthxbye, wenn Sie über den resultierenden Wert benachrichtigt werden müssen, sobald dieser abgeschlossen ist.

Beide Redis / Ruby basierten tho.

Um ehrlich zu sein, wenn Sie einen service-basierten Ansatz verfolgen, muss dieser nicht unbedingt in Ihre aktuelle Plattform integriert werden, was ich für ein Plus halte. Ich hoffe, es könnte ein Set-and-Forget-System sein, das (mit irgendeiner Art von Überwachung) läuft und Aufträge erledigt. Ich bin nicht sicher, ob es überhaupt auf der gleichen Plattform ausgeführt werden muss, da es nur die Datenbankinformationen aktualisiert / ändert.

Ich bin mir ziemlich sicher, dass Sie mit viel mehr für viel weniger davonkommen könnten, wenn Sie diese Art von Arbeit in einer separaten Entität bewirtschaften würden, zumal es den Anschein hat, als würden Sie sich mit Threading-Problemen befassen. Sowohl Resque als auch Kthxbye verschieben die Verarbeitung in separate Prozesse, damit das Betriebssystem die Parallelität verarbeiten kann.

Resque

Kthxbye


Ich muss Kthxbye versuchen, wenn auch nur wegen des großen Namens!
Nathan Palmer

so ziemlich der Hammer. Als nächstes wird der ORLY sein? Bibliothek. wahrscheinlich für Statistiken irgendeiner Art zu überwachen ...;)
Lukas

0

Ich würde einen von WAS gehosteten WCF-Dienst verwenden, der eine MSMQ-Warteschlange abhört.

Profis

  • Einfachnachrichten aus der Web-App abfeuern und vergessen

  • MSMQ / WCF-Drosselung und -wiederholung

  • Garantierte Lieferung; D

  • Dead Letter Management

  • Verteilte Verarbeitung

  • WAS / MSMQ-Aktivierung

Con's

  • MSMQ (es ist nicht tot ... noch)

Die MSMQ-Funktionen in WCF machen die Verwendung von MSMQ sehr angenehm. Ja, Sie werden an der Konfiguration scheitern, aber der Nutzen wird das Opfer überwiegen.


0

Ich bin ein paar Mal darauf gestoßen, als ich Webanwendungen entwickelte. Wir haben es gelöst, indem wir eine Windows-Konsolenanwendung erstellt haben, die die Aufgabe ausführt, und eine geplante Aufgabe erstellt, die von Zeit zu Zeit ausgeführt wird, um die Aufgabe tatsächlich auszuführen.


0

Sie können die Arbeit an einem Hintergrundthread (oder an vielen Hintergrundthreads) mit Rx und so etwas wie dem Folgenden weiterleiten:

var scheduler = new EventLoopScheduler( SchedulerThreadName );
_workToDo = new Subject<Action>();
var queueSubscription = _workToDo.ObserveOn( scheduler ).Subscribe( work => work() );
_cleanup = new CompositeDisposable( queueSubscription, scheduler );

Benutzen:

var work = () => { ... };
_workToDo.OnNext( work ); // Can also put on error / on complete in here

Hosten Sie all das in einer Klasse, von der es nur eine gibt (auch bekannt als Singleton, aber tun Sie es richtig - verwenden Sie Ihren IoC-Container, um den Lebensstil zu bestimmen).

Sie können die Größe des Thread-Pools usw. steuern, indem Sie einen benutzerdefinierten Scheduler schreiben, anstatt den EventLoopScheduler (der einen einzelnen Thread ausführt) zu verwenden.


0

Ich habe diese Art von Dingen einige Male implementiert. Unter Windows habe ich ein Python-Befehlszeilenprogramm eingerichtet, das zu verschiedenen Zeiten etwas ausführt. Dieses Programm macht auch eine xmlrpc-Schnittstelle an einem Port verfügbar. Anschließend wird jede Minute ein Job mit geplanten Tasks ausgeführt und die xmlrpc-Schnittstellen abgefragt. Wenn sie nicht aktiv sind, wird versucht, sie zu starten. Wenn nicht, schickt es mir eine E-Mail.

Der Vorteil ist, dass der ausgeführte Job nicht an Cron oder Zeitplan gebunden ist. Ich habe einen Prozessjob, der alle Sekunden ausgeführt wird, aber zwischen dem Starten eines neuen Jobs wird immer länger gewartet, je nachdem, ob Arbeit zu erledigen war. Außerdem kann es verwendet werden, um basierend auf dem Ergebnis intelligent zu handeln. Hast du einen 500er Fehler? Hast du eine wirklich lange Verspätung? Mach etwas anderes. Benachrichtigen Sie einen anderen Dienst. Usw.

Und dasselbe System funktioniert unter Unix mit geringfügigen Änderungen.


0

Ich habe selbst keine Antwort für Sie, aber das Problem hat geklingelt - ich erinnere mich, dass einige zufällige Typen es einmal in einem Podcast besprochen haben .

Spolsky: Mir ist eine der Fragen aufgefallen, die Sie im Blog gestellt haben: Wie sollten Sie mit wiederkehrenden Wartungsaufgaben im Allgemeinen umgehen?

Atwood: Ja.

Spolsky: Ist das eine faire Charakterisierung? Jede Website hat einige Aufgaben, die Sie zum Zeitpunkt des Ladens einer Webseite nicht ausführen möchten, die Sie jedoch mit einer gewissen Wiederholung ausführen möchten.

Atwood: Ja, Hintergrundaufgaben.

Spolsky: Ja, was hast du herausgefunden?

Atwood: Nun, ich habe ursprünglich auf Twitter gefragt, weil ich nur etwas Leichtes wollte. Ich wollte wirklich keinen Windows-Dienst schreiben. Ich hatte das Gefühl, dass der Bandcode nicht stimmt. Außerdem ist der Code, der die Arbeit tatsächlich erledigt, eine Webseite, denn für mich ist das eine logische Arbeitseinheit auf einer Website, die eine Webseite ist. Es ist also wirklich so, als würden wir auf die Website zurückrufen. Es ist genau wie bei einer anderen Anfrage auf der Website. Ich habe es also als etwas angesehen, das inline bleiben sollte, und als den kleinen Ansatz, den wir auf Twitter gefunden haben und der mir empfohlen wurde Um im Wesentlichen etwas mit einem festen Ablaufdatum zum Anwendungscache hinzuzufügen, müssen Sie einen Rückruf ausführen. Wenn dieses abläuft, wird eine bestimmte Funktion aufgerufen, die die Arbeit erledigt. Anschließend fügen Sie sie mit demselben Ablaufdatum wieder zum Cache hinzu.


1
Ja, das funktioniert für Sites, die viel kleiner sind als StackOverflow. Die Skalierung ist hier leider ein großes Problem (oder zum Glück, je nachdem, wie Sie es betrachten).
Kevin Montrose

@ Kevin Montrose, ich plädiere hier für völlige Domain-Ignoranz. Könnten Sie bitte erklären, warum es nicht skalierbar ist, eine geheime (möglicherweise in kleinen Einheiten) Webseite (n) für die Ausführung der Arbeit zu haben, die von einer auffrischenden Seite oder einem Cron-Job an einer anderen Stelle aufgerufen wird? Ich bezweifle nicht, dass Sie Recht haben, aber ich würde gerne lernen.
Oddthinking

Ihr bestimmter Vorschlag (der Cache-Ablauf) lässt sich nicht skalieren, da alle Cache-Abläufe (in ASP.NET) einen einzigen Thread ausführen (dies ist ein cleverer Hack für kleinere Sites, wie dies früher der Fall war). Eine Cron-Task lässt sich nicht skalieren, da wir aus einem einzelnen Server herausgewachsen sind (SO ist jetzt 3 und wächst noch) und jede Cron-Task auf einen einzelnen Server trifft (zumindest wäre es sehr schmerzhaft , diese Invariante mit unserer Auslastung zu ändern) . Balance Setup). Eine Cron-Aufgabe müsste auch sehr häufig ausgeführt werden, da diese Aufgaben in der Größenordnung von Minuten wiederholt werden.
Kevin Montrose

Es ist erwähnenswert, dass wir die Zeitplanung im "Cron-Stil" für weniger häufig ausgeführte, feste Intervalle, bereits ausgeführte Aufgaben, Dinge wie Ausweisvergabe und tägliche E-Mail-Benachrichtigungen verwenden.
Kevin Montrose

0

Task Queue Java API Übersicht

Aufgabenkonzepte
In der App Engine-Hintergrundverarbeitung ist eine Aufgabe eine vollständige Beschreibung einer kleinen Arbeitseinheit. Diese Beschreibung besteht aus zwei Teilen:

  • Eine Datennutzlast, die die Aufgabe parametrisiert.
  • Code, der die Aufgabe implementiert.

Aufgaben als Offline-Web-Hooks
Glücklicherweise bietet das Internet bereits eine solche Lösung in Form einer HTTP-Anfrage und ihrer Antwort. Die Datennutzdaten sind der Inhalt der HTTP-Anforderung, z. B. Webformularvariablen, XML, JSON oder codierte Binärdaten. Die Code-Referenz ist die URL selbst; Der eigentliche Code ist die Logik, die der Server bei der Vorbereitung der Antwort ausführt.


Ich schlage nicht vor, die GAE-Taskwarteschlangen-API zu verwenden, sondern ihrem Modell zu folgen. Sie haben eine Weile darüber nachgedacht und eine Implementierung geschrieben.
antony.trupe

0

Tue beides

Fügen Sie dem Fragenpfad einen optionalen Parameter hinzu, der die Arbeit erledigt, die Sie derzeit auf Benutzeranforderungen huckepack tragen:

Hintergrundaufgaben auf einer großen Site ausführen

Erstellen Sie eine Konsolenanwendung, die auf jedem Server ausgeführt wird und die gemeinsam genutzte IIS-Protokollbinärdatei öffnet und bis zum aktuellen Ende der Datei liest. Verwenden Sie einen Dateisystemwatcher oder ein Zeitintervall, um vorwärts zu lesen und Updates zu sammeln, während IIS das Protokoll löschte.

Verwenden Sie diese Informationen, um festzustellen, welche Seiten gerade angezeigt wurden.

Verwenden Sie die Seiten-URLs aus dem analysierten Protokoll, um die "extrastuff" -Version der URL auf localhost mit einem Webclient-Objekt aufzurufen.

Fügen Sie Code hinzu, um die Dateien am Ende jedes Protokollzeitraums zu wechseln, oder starten Sie den Prozess in jedem Protokollzeitraum neu.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.