Übersicht über den Lebenszyklus von Reliable Services

Bei der Betrachtung der Lebenszyklen von Azure Service Fabric Reliable Services sind vor allem die Grundlagen zum Lebenszyklus relevant. Im Allgemeinen enthält der Lebenszyklus Folgendes:

  • Während des Starts:
    • Dienste werden erstellt.
    • Die Dienste können keinen oder mehrere Listener erstellen und zurückgeben.
    • Alle zurückgegebenen Listener werden geöffnet und ermöglichen so eine Kommunikation mit dem Dienst.
    • Die Methode RunAsync des Diensts wird aufgerufen und ermöglicht dem Dienst die Ausführung von langwierigen Aufgaben oder von Hintergrundaufgaben.
  • Während des Herunterfahrens:
    • Das an RunAsync übergebene Abbruchtoken wird abgebrochen, und die Listener werden geschlossen.
    • Sobald die Listener geschlossen sind, wird das Dienstobjekt selbst zerstört.

Es gibt Informationen zur genauen Reihenfolge dieser Ereignisse. Die Reihenfolge der Ereignisse kann sich geringfügig ändern, je nachdem, ob der zuverlässige Dienst zustandslos oder zustandsbehaftet ist. Für zustandsbehaftete Dienste müssen wir uns außerdem mit dem Tauschszenario für primäre Replikate befassen. Während dieser Sequenz wird die Rolle des primären Replikats auf ein anderes Replikat (oder wieder zurück) übertragen, ohne dass der Dienst heruntergefahren wird. Schließlich müssen wir uns mit Fehlerbedingungen beschäftigen.

Start zustandsloser Dienste

Der Lebenszyklus eines zustandslosen Diensts ist einfach. So sieht die Reihenfolge der Ereignisse aus:

  1. Der Dienst wird erstellt.
  2. StatelessService.CreateServiceInstanceListeners() wird aufgerufen, und alle zurückgegebenen Listener werden geöffnet. ICommunicationListener.OpenAsync() wird für jeden Listener aufgerufen.
  3. Anschließend geschehen gleichzeitig zwei Dinge:
    • Die Methode StatelessService.RunAsync() des Diensts wird aufgerufen.
    • Die Methode StatelessService.OnOpenAsync() des Diensts wird aufgerufen (sofern vorhanden). Dieser Aufruf ist eine ungewöhnliche Außerkraftsetzung, aber verfügbar. Erweiterte Serviceinitialisierungsaufgaben können zu diesem Zeitpunkt gestartet werden.

Herunterfahren zustandsloser Dienste

Beim Herunterfahren eines zustandslosen Diensts wird dasselbe Muster in umgekehrter Reihenfolge befolgt:

  1. Alle geöffneten Listener werden geschlossen. ICommunicationListener.CloseAsync() wird für jeden Listener aufgerufen.
  2. Das an RunAsync() übergebene Abbruchtoken wird abgebrochen. Eine Überprüfung der Eigenschaft IsCancellationRequested des Abbruchtokens gibt „true“ zurück, und die Methode ThrowIfCancellationRequested des Tokens löst eine Ausnahme vom Typ OperationCanceledException aus, sofern sie aufgerufen wird. Service Fabric wartet darauf, dass RunAsync() abgeschlossen wird.
  3. Die Abschluss von RunAsync() wird die StatelessService.OnCloseAsync()-Methode des Diensts aufgerufen, sofern vorhanden. „OnCloseAsync“ wird aufgerufen, wenn die Instanz des zustandslosen Diensts ordnungsgemäß beendet wird. Dies kann der Fall sein, wenn Code für den Dienst aktualisiert, die Dienstinstanz aufgrund des Lastenausgleichs verschoben oder ein vorübergehender Fehler erkannt wird. Es ist nicht üblich, StatelessService.OnCloseAsync() außer Kraft zu setzen. Die Methode kann jedoch verwendet werden, um Ressourcen sicher zu schließen, die Hintergrundverarbeitung anzuhalten, das Speichern des externen Status zu beenden oder bestehende Verbindungen zu deaktivieren.
  4. Nach Abschluss von StatelessService.OnCloseAsync() wird das Dienstobjekt zerstört.

Start zustandsbehafteter Dienste

Zustandsbehaftete Dienste befolgen mit wenigen Änderungen ein ähnliches Muster wie zustandslose Dienste. Beim Starten eines zustandsbehafteten Diensts lautet die Reihenfolge der Ereignisse wie folgt:

  1. Der Dienst wird erstellt.

  2. StatefulServiceBase.OnOpenAsync() wird aufgerufen. Dieser Aufruf wird normalerweise nicht im Dienst außer Kraft gesetzt.

  3. StatefulServiceBase.CreateServiceReplicaListeners() wird aufgerufen.

    • Falls es sich bei dem Dienst um einen primären Dienst handelt, werden alle zurückgegebenen Listener geöffnet. ICommunicationListener.OpenAsync() wird für jeden Listener aufgerufen.
    • Bei einem sekundären Dienst werden nur als ListenOnSecondary = true gekennzeichnete Listener geöffnet. Für sekundäre Dienste geöffnete Listener sind eher selten.
  4. Anschließend findet parallele Ausführung statt:

    • Falls es sich bei dem Dienst gerade um einen primären Dienst handelt, wird die Methode StatefulServiceBase.RunAsync() des Diensts aufgerufen.
    • StatefulServiceBase.OnChangeRoleAsync() wird aufgerufen. Dieser Aufruf wird normalerweise nicht im Dienst außer Kraft gesetzt.

    Hinweis

    Für ein neues sekundäres Replikat wird StatefulServiceBase.OnChangeRoleAsync() zwei Mal aufgerufen. Ein Mal nach Schritt 2, wenn es zu einem sekundären Replikat im Leerlauf wird, und ein weiteres Mal während Schritt 4, wenn es zu einem aktiven sekundären Replikat wird. Weitere Informationen zu Replikaten und zum Instanzlebenszyklus finden Sie unter Replikate und Instanzlebenszyklus.

Herunterfahren zustandsbehafteter Dienste

Wie bei zustandslosen Diensten stimmen die Lebenszyklusereignisse während des Herunterfahrens mit denen während des Starts überein, aber in umgekehrter Reihenfolge. Wenn ein zustandsbehafteter Dienst heruntergefahren wird, treten die folgenden Ereignisse auf:

  1. Alle geöffneten Listener werden geschlossen. ICommunicationListener.CloseAsync() wird für jeden Listener aufgerufen.

  2. Die StatefulServiceBase.OnCloseAsync()-Methode wird aufgerufen. Dieser Aufruf ist eine ungewöhnliche Außerkraftsetzung, aber er ist verfügbar.

  3. Das an RunAsync() übergebene Abbruchtoken wird abgebrochen. Eine Überprüfung der Eigenschaft IsCancellationRequested des Abbruchtokens gibt „true“ zurück, und die Methode ThrowIfCancellationRequested des Tokens löst eine Ausnahme vom Typ OperationCanceledException aus, sofern sie aufgerufen wird. Service Fabric wartet darauf, dass RunAsync() abgeschlossen wird.

    Hinweis

    Der Abschluss von RunAsync muss nur abgewartet werden, wenn dieses Replikat ein primäres Replikat ist.

  4. Nach Abschluss von StatefulServiceBase.RunAsync() wird das Dienstobjekt zerstört.

Tausch des primären Replikats bei zustandsbehafteten Diensten

Während ein zustandsbehafteter Dienst ausgeführt wird, werden nur für die primären Replikate dieser zustandsbehafteten Dienste die Kommunikationslistener geöffnet und die Methode RunAsync aufgerufen. Die sekundären Replikate werden erstellt, erhalten jedoch keine weiteren Aufrufe. Während ein zustandsbehafteter Dienst ausgeführt wird, kann sich das Replikat, das zurzeit das primäre Replikat ist, als Ergebnis eines Fehlers oder einer Clusterausgleichsoptimierung ändern. Was bedeutet dies im Hinblick auf die für ein Replikat sichtbaren Lebenszyklusereignisse? Das für das zustandsbehaftete Replikat sichtbare Verhalten hängt davon ab, ob das Replikat während des Tauschs höher oder tiefer gestuft wird.

Für das tiefer gestufte primäre Replikat

Für das tiefer gestufte primäre Replikat muss Service Fabric die Verarbeitung von Nachrichten und jegliche Hintergrundverarbeitung beenden. Folglich ähnelt dieser Schritt dem Herunterfahren des Diensts. Ein Unterschied besteht darin, dass der Dienst nicht zerstört oder geschlossen wird, weil er als sekundäres Replikat erhalten bleibt. Die folgenden APIs werden aufgerufen:

  1. Alle geöffneten Listener werden geschlossen. ICommunicationListener.CloseAsync() wird für jeden Listener aufgerufen.
  2. Das an RunAsync() übergebene Abbruchtoken wird abgebrochen. Eine Überprüfung der Eigenschaft IsCancellationRequested des Abbruchtokens gibt „true“ zurück, und die Methode ThrowIfCancellationRequested des Tokens löst eine Ausnahme vom Typ OperationCanceledException aus, sofern sie aufgerufen wird. Service Fabric wartet darauf, dass RunAsync() abgeschlossen wird.
  3. Listener, die als „ListenOnSecondary = true“ gekennzeichnet sind, werden geöffnet.
  4. Das StatefulServiceBase.OnChangeRoleAsync()-Element des Diensts wird aufgerufen. Dieser Aufruf wird normalerweise nicht im Dienst außer Kraft gesetzt.

Für das höher gestufte sekundäre Replikat

Analog dazu muss das höher gestufte sekundäre Replikat für Service Fabric mit dem Lauschen auf Nachrichten beginnen und alle Hintergrundaufgaben starten, die abgeschlossen werden müssen. Demzufolge ähnelt dieser Prozess dem Erstellen des Diensts, mit der Ausnahme, dass das Replikat selbst bereits vorhanden ist. Die folgenden APIs werden aufgerufen:

  1. ICommunicationListener.CloseAsync() wird für alle geöffneten Listener aufgerufen (die mit „ListenOnSecondary = true“ markiert sind).
  2. Alle Kommunikationslistener werden geöffnet. ICommunicationListener.OpenAsync() wird für jeden Listener aufgerufen.
  3. Anschließend findet parallele Ausführung statt:
    • Die Methode StatefulServiceBase.RunAsync() des Diensts wird aufgerufen.
    • StatefulServiceBase.OnChangeRoleAsync() wird aufgerufen. Dieser Aufruf wird normalerweise nicht im Dienst außer Kraft gesetzt.

Hinweis

CreateServiceReplicaListeners wird nur ein einziges Mal aufgerufen und während der Replikathöherstufung oder -herabstufung nicht erneut aufgerufen. Dieselben ServiceReplicaListener-Instanzen werden verwendet, aber neue ICommunicationListener-Instanzen werden erstellt (durch Aufrufen der ServiceReplicaListener.CreateCommunicationListener-Methode), nachdem die vorherigen Instanzen geschlossen wurden.

Allgemeine Probleme beim Herunterfahren zustandsbehafteter Dienste und beim Tieferstufen des primären Replikats

Service Fabric ändert das primäre Replikat eines zustandsbehafteten Diensts aus verschiedenen Gründen. Zu den gängigsten Gründen zählen Lastenausgleich und Anwendungsupgrade. Während dieser Vorgänge (sowie beim normalen Herunterfahren des Diensts – etwa, wenn der Dienst gelöscht wird) muss der Dienst das Abbruchtoken (CancellationToken) berücksichtigen.

Bei Diensten ohne ordnungsgemäße Abbruchbehandlung können mehrere Probleme auftreten. Diese Vorgänge werden verlangsamt, da Service Fabric auf eine ordnungsgemäße Beendigung der Dienste wartet. Dies kann letztendlich zu nicht erfolgreichen Upgrades mit Timeout und Rollback führen. Wenn das Abbruchtoken nicht berücksichtigt wird, können auch unausgeglichene Cluster entstehen. Cluster sind unausgeglichen, da Knoten ausgelastet werden, die Dienste aber nicht ausgeglichen werden können, weil ihre Verschiebung zu lange dauert.

Da es sich um zustandsbehaftete Dienste handelt, ist es auch wahrscheinlich, dass sie die zuverlässigen Sammlungen verwenden. Beim Tieferstufen eines primären Replikats wird in Service Fabric als eine der ersten Maßnahmen der Schreibzugriff auf den zugrunde liegenden Zustand entzogen. Dies führt zu weiteren Problemen, die Einfluss auf den Lebenszyklus des Diensts haben können. Die Sammlungen geben Ausnahmen auf der Grundlage des Timings und abhängig davon zurück, ob das Replikat verschoben oder heruntergefahren wird. Diese Ausnahmen müssen korrekt behandelt werden. Von Service Fabric ausgelöste Ausnahmen können dauerhaft (FabricException) oder vorübergehend (FabricTransientException) sein. Dauerhafte Ausnahmen müssen protokolliert und ausgelöst werden. Vorgänge mit vorübergehenden Ausnahmen können hingegen mit einer Wiederholungslogik wiederholt werden.

Die Behandlung von Ausnahmen, die auf die Verwendung von ReliableCollections in Verbindung mit Dienstlebenszyklusereignissen zurückzuführen sind, ist ein wichtiger Bestandteil der Tests und Prüfungen für einen zuverlässigen Dienst. Es empfiehlt sich, den Dienst während der Durchführung von Upgrades und Chaostests immer unter Last auszuführen, bevor Sie ihn in einer Produktionsumgebung bereitstellen. Mit diesen einfachen Schritten können Sie sicherstellen, dass Ihr Dienst ordnungsgemäß implementiert ist und Lebenszyklusereignisse korrekt behandelt.

Hinweise zum Dienstlebenszyklus

  • Sowohl die RunAsync()-Methode als auch die CreateServiceReplicaListeners/CreateServiceInstanceListeners-Aufrufe sind optional. Ein Dienst kann eine dieser Optionen, beide oder keine von beiden aufweisen. Wenn der Dienst beispielsweise alle Vorgänge infolge von Benutzeraufrufen ausführt, muss RunAsync() nicht implementiert werden. Nur die Kommunikationslistener und der zugehörige Code sind erforderlich. Ebenso ist das Erstellen und Zurückgeben von Kommunikationslistenern optional, da der Dienst möglicherweise nur Hintergrundaufgaben ausführt und daher nur RunAsync() implementieren muss.
  • Ein Dienst kann RunAsync() erfolgreich abschließen und dann zurückkehren. Der Abschluss ist keine Fehlerbedingung. Der Abschluss von RunAsync() gibt an, dass die Hintergrundaufgaben des Diensts abgeschlossen sind. Bei zustandsbehafteten zuverlässigen Diensten wird RunAsync() erneut aufgerufen, wenn das Replikat vom primären Replikat zum sekundären Replikat tiefer gestuft und dann wieder zum primären Replikat höher gestuft wird.
  • Wenn ein Dienst die Ausführung von RunAsync() mit einer unerwarteten Ausnahme beendet, liegt ein Fehler vor. Das Dienstobjekt wird heruntergefahren, und ein Integritätsfehler wird gemeldet.
  • Zwar gibt es keine zeitliche Begrenzung für die Rückgabe dieser Methoden, aber Sie verlieren sofort die Möglichkeit zum Schreiben in zuverlässige Sammlungen und können daher Ihre eigentlichen Arbeiten nicht abschließen. Es wird empfohlen, dass Sie sie so schnell wie möglich nach dem Empfang der Abbruchanforderung zurückgeben. Wenn der Dienst nicht in einem angemessenen Zeitraum auf diese API-Aufrufe reagiert, kann Service Fabric das Beenden des Diensts erzwingen. Dies geschieht normalerweise nur während Anwendungsupgrades oder beim Löschen eines Diensts. Das Timeout beträgt standardmäßig 15 Minuten.
  • Fehler im OnCloseAsync()-Pfad führen zu einem Aufruf von OnAbort(). So erhält der Dienst eine letzte Gelegenheit zum Bereinigen und Freigeben aller beanspruchten Ressourcen. Diese Methode wird im Allgemeinen verwendet, wenn auf dem Knoten ein dauerhafter Fehler erkannt wird oder Service Fabric den Lebenszyklus der Dienstinstanz aufgrund von internen Fehlern nicht zuverlässig verwalten kann.
  • OnChangeRoleAsync() wird immer dann aufgerufen, wenn das Replikat des zustandsbehafteten Diensts die Rolle wechselt und beispielsweise ein primäres oder sekundäres Replikat wird. Primäre Replikate erhalten Schreibstatus (mit Erlaubnis zum Erstellen und Schreiben in Reliable Collections). Sekundäre Replikate erhalten Lesestatus (können nur aus vorhandenen Reliable Collections lesen). Die meisten Aufgaben in einem zustandsbehafteten Dienst werden im primären Replikat ausgeführt. Sekundäre Replikate können schreibgeschützte Überprüfungen durchführen, Berichte generieren und Data Mining oder andere schreibgeschützte Aufträge ausführen.

Nächste Schritte