ステートレス ワーカー グレイン

既定で Orleans ランタイムでは、クラスター内でグレインのアクティブ化が 1 つのみ作成されます。 これは、一意の型と ID を持つエンティティに各グレインが対応している Virtual Actor モデルを最も直感的に表現したものです。 ただし、システム内の特定のエンティティに関連付けられていない機能ステートレス操作をアプリケーションで実行する必要がある場合もあります。 たとえば、圧縮されたペイロードを含む要求をクライアントが送信し、処理のためにターゲット グレインにルーティングする前にこれらを圧縮解除する必要がある場合、このような圧縮解除とルーティングのロジックは、アプリケーション内の特定のエンティティに関連付けられておらず、簡単にスケールアウトできます。

StatelessWorkerAttribute がグレイン クラスに適用されると、そのクラスのグレインをステートレス ワーカー グレインとして扱う必要があることが Orleans ランタイムに指示されます。 ステートレス ワーカー グレインには次のプロパティがあるため、通常のグレイン クラスとは実行方法が大きく異なります。

  1. Orleans ランタイムでは、クラスターのさまざまなサイロでステートレス ワーカー グレインの複数のアクティブ化を作成でき、また作成されます。
  2. ステートレス worker グレインに対して行われた要求は、サイロに互換性がある限り、ローカルで実行されるため、ネットワークまたはシリアル化のコストは発生しません。 ローカル サイロに互換性がない場合、要求は互換性のあるサイロに転送されます。
  3. Orleans ランタイムでは、ステートレス ワーカー グレインの既存のアクティブ化がビジー状態の場合、追加のアクティブ化が自動的に作成されます。 ランタイムでサイロごとに作成されるステートレス ワーカー グレインのアクティブ化の最大数は、省略可能な maxLocalWorkers 引数によって明示的に指定されていない限り、既定でマシン上の CPU コアの数により制限されます。
  4. 2 と 3 の理由により、ステートレス ワーカー グレインのアクティブ化は個別にアドレス指定できません。 ステートレス ワーカー グレインに対する 2 つの連続する要求は、異なるアクティブ化によって処理される場合があります。

ステートレス ワーカー グレインは、実際の負荷に基づいて自動的にスケールアップおよびスケールダウンされるグレイン アクティブ化の自動管理プールを簡単に作成する方法を提供します。 ランタイムでは、使用可能なステートレス ワーカー グレインのアクティブ化が常に同じ順序でスキャンされます。 そのため、最初に見つかるアイドル状態のローカル アクティブ化に常に要求がディスパッチされ、以前のすべてのアクティブ化がビジー状態の場合にのみ最後のアクティブ化に到達します。 すべてのアクティブ化がビジー状態で、アクティブ化の制限に達していない場合は、リストの末尾にもう 1 つのアクティブ化が作成され、それに要求がディスパッチされます。 つまり、ステートレス ワーカー グレインに対する要求の速度が高まり、既存のアクティブ化が現在すべてビジー状態になると、ランタイムではアクティブ化のプールが制限まで拡張されます。 逆に、負荷が低下し、ステートレス ワーカー グレインのより少数のアクティブ化によって処理できるようになると、リストの末尾にあるアクティブ化には要求がディスパッチされなくなります。 これらはアイドル状態になり、最終的には標準のアクティブ化収集プロセスによって非アクティブ化されます。 そのため、最終的には、アクティブ化のプールは負荷に合わせて縮小されます。

次の例では、既定の最大アクティブ化数制限のあるステートレス ワーカー グレイン クラス MyStatelessWorkerGrain を定義します。

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

ステートレス ワーカー グレインの呼び出しは、他のグレインの呼び出しと同じです。 唯一の違いは、ほとんどの場合、単一のグレイン ID が使用されることです (例: 0 または Guid.Empty)。 複数のステートレス ワーカー グレイン プールがある場合は、複数のグレイン ID を使用できます (ID ごとに 1 つが望ましい)。

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

これにより、サイロごとにグレイン アクティブ化を 1 つのみ含むステートレス ワーカー グレイン クラスが定義されます。

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

StatelessWorkerAttribute では、ターゲット グレイン クラスの再入が変更されないことに注意してください。 他のグレインと同様に、ステートレス ワーカー グレインは既定で再入不可能です。 グレイン クラスに ReentrantAttribute を追加することで、明示的に再入可能にすることができます。

State

"ステートレス ワーカー" の "ステートレス" 部分は、ステートレス ワーカーが状態を持つことができず、機能操作の実行のみに限定されることを意味するものではありません。 他のグレインと同様に、ステートレス ワーカー グレインでは、必要な任意の状態を読み込んでメモリ内に保持することができます。 これは、クラスターの同じサイロ上および異なるサイロ上にステートレス ワーカー グレインの複数のアクティブ化を作成できるので、さまざまなアクティブ化によって保持される状態を調整する簡単なメカニズムがないためです。

いくつかの有用なパターンには、ステートレス ワーカーが状態を保持することが含まれます。

ホット キャッシュ項目のスケールアウト

高スループットが発生するホット キャッシュ項目の場合、このような各項目をステートレス ワーカー グレインに保持すると、次のようになります。

  1. サイロ内、およびクラスター内のすべてのサイロ間で、自動的にスケールアウトします。
  2. クライアント ゲートウェイ経由でクライアント要求を受信したサイロで、データが常にローカルに使用可能になるため、別のサイロへの追加のネットワーク ホップなしで要求に応答できるようになります。

スタイルの集計を減らす

シナリオによっては、アプリケーションでクラスター内の特定の種類のすべてのグレインにわたって特定のメトリックを計算し、集計を定期的に報告する必要があります。 たとえば、ゲーム マップごとの複数のプレーヤーや、VoIP 通話の平均継続時間などを報告します。数千または数百万のグレインによってそれぞれのメトリックが 1 つのグローバル アグリゲーターに報告されると、アグリゲーターはすぐに過負荷になり、大量のレポートを処理できなくなります。 もう 1 つのアプローチは、このタスクを 2 つ以上のステップに変換して、スタイルの集計を減らすことです。 集計の最初のレイヤーでは、ステートレス ワーカーの事前集計グレインにメトリックを送信しているグレインをレポートします。 Orleans ランタイムでは、各サイロでステートレス ワーカー グレインの複数のアクティブ化が自動的に作成されます。 このような呼び出しはすべて、リモート呼び出しやメッセージのシリアル化なしでローカルに処理されるため、このような集計のコストはリモートの場合よりも大幅に少なくなります。 これで、事前集計ステートレス ワーカー グレインの各アクティブ化では、個別に、または他のローカル アクティブ化と連携して、グローバル最終アグリゲーター (または、必要に応じて別の削減レイヤー) に、オーバーロードすることなく集計レポートを送信できます。