Grains für zustandslose Worker

Standardmäßig erstellt die Orleans-Runtime maximal eine einzelne Aktivierung eines Grains innerhalb des Clusters. Dies ist der intuitivste Ausdruck des Modells virtueller Akteure, wobei jedes Grain einer Entität mit einem eindeutigen Typ bzw. mit einer eindeutigen Identität entspricht. Es gibt jedoch auch Fälle, in denen eine Anwendung funktionale zustandslose Vorgänge ausführen muss, die nicht an eine bestimmte Entität im System gebunden sind. Wenn der Client beispielsweise Anforderungen mit komprimierten Nutzdaten sendet, die dekomprimiert werden müssen, bevor sie für die Verarbeitung an das Ziel-Grain weitergeleitet werden können, ist eine solche Dekomprimierungs-/Routinglogik nicht an eine bestimmte Entität in der Anwendung gebunden und kann problemlos horizontal skaliert werden.

Wenn StatelessWorkerAttribute auf eine Grain-Klasse angewendet wird, wird der Orleans-Runtime dadurch signalisiert, dass Grains dieser Klasse als Grains für zustandslose Worker behandelt werden sollen. Die folgenden Eigenschaften von Grains für zustandslose Worker führen dazu, dass sich ihre Ausführung stark von normalen Grain-Klassen unterscheidet:

  1. Die Orleans-Runtime kann und wird mehrere Aktivierungen eines Grains für zustandslose Worker in verschiedenen Silos des Clusters erstellen.
  2. Anfragen an zustandslose Arbeiterkörner werden lokal ausgeführt, solange das Silo kompatibel ist und daher keine Netzwerk- oder Serialisierungskosten entstehen. Wenn das lokale Silo nicht kompatibel ist, werden Anforderungen an einen kompatiblen Silo weitergeleitet.
  3. Die Orleans-Runtime erstellt automatisch zusätzliche Aktivierungen eines Grains für zustandslose Worker, wenn die bereits vorhandenen ausgelastet sind. Die maximale Anzahl von Aktivierungen eines Grains für zustandslose Worker, die die Runtime pro Silo erstellt, ist standardmäßig durch die Anzahl von CPU-Kernen auf dem Computer begrenzt, sofern sie nicht explizit durch das optionale Argument maxLocalWorkers angegeben wurde.
  4. Aufgrund der Punkte 2 und 3 sind Aktivierungen von Grains für zustandslose Worker nicht individuell adressierbar. Zwei aufeinanderfolgende Anforderungen an ein Grain für zustandslose Worker können durch unterschiedliche Aktivierungen verarbeitet werden.

Grains für zustandslose Worker bieten eine einfache Möglichkeit zum Erstellen eines automatisch verwalteten Pools mit Grain-Aktivierungen, der basierend auf der tatsächlichen Last automatisch hoch- und herunterskaliert wird. Die Runtime sucht immer in der gleichen Reihenfolge nach verfügbaren Aktivierungen von Grains für zustandslose Worker. Aus diesem Gründen werden Anforderungen immer an die erste lokale Aktivierung im Leerlauf gesendet, die gefunden wird. Die letzte Aktivierung wird nur verwendet, wenn alle vorherigen Aktivierungen ausgelastet sind. Wenn alle Aktivierungen ausgelastet sind und das Aktivierungslimit nicht erreicht wurde, wird am Ende der Liste eine weitere Aktivierung erstellt und die Anforderung an sie gesendet. Das bedeutet, dass die Runtime den Pool ihrer Aktivierungen erweitert, bis der Grenzwert erreicht ist, wenn die Rate der Anforderungen an ein Grain für zustandslose Worker zunimmt und derzeit alle vorhandenen Aktivierungen ausgelastet sind. Wenn sich die Auslastung hingegen verringert und von einer geringeren Anzahl von Aktivierungen des Grains für zustandslose Worker bewältigt werden kann, werden an die Aktivierungen am Ende der Liste keine Anforderungen mehr gesendet. Sie werden in den Leerlauf versetzt und schließlich durch den standardmäßigen Aktivierungsbereinigungsprozess deaktiviert. Somit wird der Pool mit den Aktivierungen schließlich verkleinert, um der Auslastung zu entsprechen.

Im folgenden Beispiel wird eine Grain-Klasse für zustandslose Worker (MyStatelessWorkerGrain) mit dem Standardgrenzwert für die maximale Anzahl von Aktivierungen definiert:

[StatelessWorker]
public class MyStatelessWorkerGrain : Grain, IMyStatelessWorkerGrain
{
    // ...
}

Die Vorgehensweise zum Aufrufen eines Grains für zustandslose Worker ist die gleiche wie bei jedem anderen Grain. Der einzige Unterschied besteht darin, dass in den meisten Fällen eine einzelne Korn-ID, z. B. 0 oder Guid.Empty, verwendet wird. Mehrere Grain-IDs können verwendet werden, wenn mehrere Pools mit Grains für zustandslose Worker vorhanden sind (jeweils einer pro ID ist wünschenswert).

var worker = GrainFactory.GetGrain<IMyStatelessWorkerGrain>(0);
await worker.Process(args);

Hier wird eine Grain-Klasse für zustandslose Worker mit maximal einer Grain-Aktivierung pro Silo definiert:

[StatelessWorker(1)] // max 1 activation per silo
public class MyLonelyWorkerGrain : ILonelyWorkerGrain
{
    //...
}

Beachten Sie, dass StatelessWorkerAttribute die Eintrittsvarianz der Grain-Zielklasse nicht ändert. Grains für zustandslose Worker sind genau wie andere Grains standardmäßig nicht eintrittsvariant. Sie können explizit als eintrittsvariant festgelegt werden, indem der Grain-Klasse ReentrantAttribute hinzugefügt wird.

State

Das Adjektiv „zustandslos“ von „zustandsloser Worker“ bedeutet nicht, dass ein zustandsloser Worker keinen Zustand haben kann und nur auf die Ausführung funktionaler Vorgänge beschränkt ist. Ein Grain für zustandslose Worker kann wie jedes andere Grain jeden benötigten Zustand laden und im Arbeitsspeicher speichern. Da jedoch mehrere Aktivierungen eines Grains für zustandslose Worker im gleichen Silo sowie in verschiedenen Silos des Clusters erstellt werden können, gibt es keinen einfachen Mechanismus, um den Zustand verschiedener Aktivierungen zu koordinieren.

Mehrere praktische Muster beinhalten zustandslose Worker mit einem gespeicherten Zustand.

Horizontal skalierte Elemente des Caches für heiße Daten

Bei Elementen des Caches für heiße Daten mit hohem Durchsatz bewirkt die Speicherung jedes dieser Elemente in einem Grain für zustandslose Worker Folgendes:

  1. Automatisches Aufskalieren innerhalb eines Silos und über alle Silos im Cluster hinweg
  2. Die Daten sind immer lokal in dem Silo verfügbar, das die Clientanforderung über das zugehörige Clientgateway empfangen hat, sodass die Anforderungen ohne zusätzlichen Netzwerkhop zu einem anderen Silo beantwortet werden können.

Verringern der Stilaggregation

In manchen Szenarien müssen Anwendungen bestimmte Metriken für alle Grains eines bestimmten Typs im Cluster berechnen und die Aggregate regelmäßig melden. Beispiele wären etwa die Meldung mehrerer Spieler*innen pro Map und die durchschnittliche Dauer eines VoIP-Anrufs. Wenn jedes der vielen Tausend oder Millionen von Grains seine Metriken an einen einzelnen globalen Aggregator melden würde, wäre der Aggregator sofort überladen und könnte die Flut an Berichten nicht verarbeiten. Der alternative Ansatz besteht darin, diese Aufgabe in einen mindestens zweistufigen Prozess umzuwandeln, um die Stilaggregation zu reduzieren. In der ersten Stufe der Aggregation sendet das meldende Grain seine Metriken an ein Grain für zustandslose Worker, bevor die Aggregation erfolgt. Die Orleans-Runtime erstellt mit jedem Silo automatisch mehrere Aktivierungen des Grains für zustandslose Worker. Da alle derartigen Aufrufe lokal ohne Remoteaufrufe oder Serialisierung der Nachrichten verarbeitet werden, sind die Kosten für eine solche Aggregation erheblich geringer als in einem Remoteszenario. Jetzt kann jede der Aktivierungen von Grains für zustandslose Worker vor der Aggregation unabhängig oder in Abstimmung mit anderen lokalen Aktivierungen ihre aggregierten Berichte an den globalen endgültigen Aggregator (oder bei Bedarf an eine andere Reduktionsebene) senden, ohne ihn zu überladen.