グレイン配置

Orleans を使用すると、グレイン呼び出しが行われたときに、クラスター内のサーバー上のメモリにそのグレインのインスタンスが確実に存在し、要求を処理できます。 クラスターでグレインが現在アクティブでない場合は、Orleans によって、グレインをアクティブ化するいずれかのサーバーが選択されます。 これを ''グレイン配置'' といいます。 配置は、負荷分散の 1 つの方法でもあります。ビジー状態のグレインの均等配置は、クラスター全体のワークロードを均等にするのに役立ちます。

Orleans の配置プロセスは完全に構成可能です。開発者がランダム、ローカル優先、読み込みベースなどの一連の既定の配置ポリシーから選択することも、カスタム ロジックを構成することもできます。 これにより、グレインを作成する場所を柔軟に決定できます。 たとえば、グレインは、運用する必要があるリソースや、通信に使用する他のグレインに近いサーバーに配置できます。 既定では、Orleans によってランダムな互換性のあるサーバーが選択されます。

Orleans で使用される配置戦略は、グローバルまたはグレイン クラス単位で構成できます。

ランダム配置

サーバーは、クラスター内の互換性のあるサーバーからランダムに選択されます。 この配置戦略は、グレインに RandomPlacementAttribute を追加することによって構成されます。

ローカル配置

ローカル サーバーに互換性がある場合は、ローカル サーバーを選択し、それ以外の場合はランダム サーバーを選択します。 この配置戦略は、グレインに PreferLocalPlacementAttribute を追加することによって構成されます。

ハッシュベースの配置

グレイン ID を負以外の整数にハッシュし、互換性のあるサーバーの数で剰余します。 サーバー アドレスで並べ替えられた互換性のあるサーバーのリストから、対応するサーバーを選択します。 クラスター メンバーシップは変わるため、安定した状態が保たれるとは限らないことに注意してください。 具体的には、サーバーを追加、削除、または再起動すると、特定のグレイン ID に対して選択されたサーバーが変わる可能性があります。この戦略を使用して配置されたグレインはグレイン ディレクトリに登録されるため、通常、メンバーシップの変更による配置決定の変更は大きな影響を及ぼすことはありません。

この配置戦略は、グレインに HashBasedPlacementAttribute を追加することによって構成されます。

アクティブ化数ベースの配置

この配置戦略は、最近のビジー状態のグレイン数に基づいて、負荷が最も低いサーバーに新しいグレイン アクティブ化を配置することを目的としています。 これには、すべてのサーバーがアクティブ化の合計数を他のすべてのサーバーに定期的に発行するメカニズムが含まれています。 その後、配置ディレクターは、ごく最近報告されたアクティブ化数を調べ、現在のサーバー上の配置ディレクターによって行われた最近のアクティブ化数に基づいて現在のアクティブ化数を予測することで、アクティブ化が最も少ないと予測されるサーバーを選択します。 ディレクターは、この予測を行うときに複数のサーバーをランダムに選択し、複数の個別のサーバーが同じサーバーをオーバーロードしないようにします。 既定では、2 つのサーバーがランダムに選択されますが、この値は ActivationCountBasedPlacementOptions を介して構成できます。

このアルゴリズムは、Michael David Mitzenmacher による「ランダム化負荷分散における 2 つの選択肢の力という論文に基づいており、「NGINX と "2 つの選択肢の力" 負荷分散アルゴリズム」という記事で説明されているように、Nginx でも分散負荷分散に使用されます。

この配置戦略は、グレインに ActivationCountBasedPlacementAttribute を追加することによって構成されます。

ステートレス ワーカー配置

ステートレス ワーカー配置は、ステートレス ワーカー グレインで使用される特別な配置戦略です。 この配置は、各サーバーで同じグレインの複数のアクティブ化が行われている可能性があり、グレインが必要ないためグレイン ディレクトリに登録されないことを除き、PreferLocalPlacement とほぼ同じように動作します。

この配置戦略は、グレインに StatelessWorkerAttribute を追加することによって構成されます。

サイロ ロールベースの配置

特定のロールを持つサイロにグレインを配置する決定論的配置戦略。 この配置戦略は、グレインに SiloRoleBasedPlacementAttribute を追加することによって構成されます。

配置戦略を選択する

Orleans によって提供される既定値を超えて、適切なグレイン配置戦略を選択するには、監視と開発者の評価が必要です。 配置戦略の選択は、アプリのサイズと複雑さ、ワークロードの特性、デプロイ環境に基づく必要があります。

ランダム配置は大数の法則に依存するため、通常は、予測できない負荷が多数のグレイン (10,000 以上) に分散している場合に適した既定値です。

アクティブ化数ベースの配置にもランダムな要素があり、分散負荷分散に一般的に使用されるアルゴリズムであり、一般的なロード バランサーで使用される 2 つの選択肢の力の原則に依存します。 サイロでは、次のような実行時統計がクラスター内の他のサイロに頻繁に発行されます。

  • 使用可能なメモリ、合計物理メモリ、メモリ使用量。
  • CPU 使用率。
  • アクティブ化の合計数と最近のアクティブなアクティブ化の数。
    • アクティブ化作業セットと呼ばれる、過去数秒にアクティブになったアクティブ化のスライディング ウィンドウ。

これらの統計から、特定のサイロの負荷を判断するために現在使用されているのは、アクティブ化の数だけです。

最終的には、さまざまな戦略を試し、パフォーマンス メトリックを監視して最適かどうかを判断する必要があります。 適切なグレイン配置戦略を選択することで、Orleans アプリのパフォーマンス、スケーラビリティ、および費用対効果を最適化できます。

既定の配置戦略を構成する

既定がオーバーライドされない限り、Orleans ではランダムな配置を使用します。 既定の配置戦略は、構成時に PlacementStrategy の実装を登録することでオーバーライドできます。

siloBuilder.ConfigureServices(services =>
    services.AddSingleton<PlacementStrategy, MyPlacementStrategy>());

グレインの配置戦略を構成する

グレイン タイプの配置戦略は、グレイン クラスに適切な属性を追加することによって構成されます。 関連する属性は、配置戦略セクションで指定されています。

カスタム配置戦略のサンプル

まず、1 つのメソッドを必要とする IPlacementDirector インターフェイスを実装するクラスを定義します。 この例では、作成しようとしているグレインの Guid を指定してサイロ番号を返す関数 GetSiloNumber が定義されていることを前提としています。

public class SamplePlacementStrategyFixedSiloDirector : IPlacementDirector
{
    public Task<SiloAddress> OnAddActivation(
        PlacementStrategy strategy,
        PlacementTarget target,
        IPlacementContext context)
    {
        var silos = context.GetCompatibleSilos(target).OrderBy(s => s).ToArray();
        int silo = GetSiloNumber(target.GrainIdentity.PrimaryKey, silos.Length);

        return Task.FromResult(silos[silo]);
    }
}

その後、グレイン クラスを戦略に割り当てられるように、次の 2 つのクラスを定義する必要があります。

[Serializable]
public sealed class SamplePlacementStrategy : PlacementStrategy
{
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class SamplePlacementStrategyAttribute : PlacementAttribute
{
    public SamplePlacementStrategyAttribute() :
        base(new SamplePlacementStrategy())
    {
    }
}

その後、この戦略を使用するグレイン クラスに属性をタグ付けするだけです。

[SamplePlacementStrategy]
public class MyGrain : Grain, IMyGrain
{
    // ...
}

最後に、SiloHost をビルドするときに戦略を登録します。

private static async Task<ISiloHost> StartSilo()
{
    var builder = new HostBuilder(c =>
    {
        // normal configuration methods omitted for brevity
        c.ConfigureServices(ConfigureServices);
    });

    var host = builder.Build();
    await host.StartAsync();

    return host;
}

private static void ConfigureServices(IServiceCollection services)
{
    services.AddSingletonNamedService<
        PlacementStrategy, SamplePlacementStrategy>(
            nameof(SamplePlacementStrategy));

    services.AddSingletonKeyedService<
        Type, IPlacementDirector, SamplePlacementStrategyFixedSiloDirector>(
            typeof(SamplePlacementStrategy));
}

配置コンテキストをより多く使用している 2 番目の簡単な例については、Orleans ソース リポジトリPreferLocalPlacementDirector を参照してください