グレインの ID

Orleans 内のそれぞれのグレインには、次の 2 つの部分で構成される、一意のユーザー定義識別子が 1 つあります:

  1. グレイン クラスを一意に識別するグレイン の名前。
  2. そのグレインクラスの論理インスタンスを一意に識別するグレイン キー

グレインの種類とキーは、どちらも Orleans で人間が判読できる文字列として表され、規則により、グレイン ID はグレイんの種類とキーが / 文字で区切られた状態で書き込まれます。 たとえば、 shoppingcart/bob65 は、キー bob65 を持つ shoppingcartという名前のグレイン型を表します。

グレイン ID を直接構築することは一般的ではありません。 代わりに、 Orleans.IGrainFactory を使用して グレイン参照 を作成する方が一般的です。

次のセクションでは、グレインの種類の名前とグレイン キーについて詳しく説明します。

グレイン型の名前

Orleans は、クラス名からサフィックス "Grain" が存在する場合は削除し、結果の文字列を小文字に変換することで、グレイン実装クラスに基づいてグレイン型名を作成します。 たとえば、 ShoppingCartGrain という名前のクラスには、グレイン型の名前 shoppingcart が付けられます。 グレイン型の名前とキーは、英数字 (a-zA-Z、 および 0-9)、などの印刷可能な文字と記号 (-_@= など) のみで構成することをおすすめします。 他の文字はサポートされている場合とサポートされない場合があり、ログに出力したり、データベースなどの他のシステムで識別子として表示したりするときに、多くの場合、特別な処理が必要になります。

または、次の例のように、 Orleans.GrainTypeAttribute 属性を使用して、アタッチ先のグレイン クラスのグレイン型名をカスタマイズすることもできます:

[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
    // Add your grain implementation here
}

前の例では、グレイン クラスの ShoppingCartGrain のグレイン型の名前は cart です。 各グレインは、グレインの種類名を 1 つだけ持つことができます。

ジェネリック グレインの場合は、ジェネリック アリティをグレイン型名に含める必要があります。 たとえば、次の DictionaryGrain<K, V> クラスについて考えてみましょう:

[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
    // Add your grain implementation here
}

グレイン クラスには 2 つのジェネリック パラメーターがあるため、グレインクラス [GrainType("dict`2")] の属性で指定されているように、グレイン型名 dict`2を作成するために、グレイン型名 dict の末尾にバックティック `とそれに続くジェネリックアリティ 2 が追加されます。

グレイン キー

便宜上、 Orleans では、 String に加えて、Guid または Int64 からのグレイン キーの構築を可能にするメソッドを公開します。 主キーのスコープはグレインの種類です。 したがって、グレインの完全な ID は、グレインの型とそのキーから形成されます。

どのスキームを使う必要があるかは、グレインの呼び出し元が決めます。 オプションは次のとおりです。

基になるデータは同じであるため、スキームは相互に使用できます。これらはすべて文字列としてエンコードされます。

シングルトン グレイン インスタンスを必要とする状況では、"default" などの既知の固定値を使用できます。 これは単なる規則ですが、この規則に従うことで、呼び出し元のサイトでシングルトン グレインが使用されていることが明らかになります。

グローバル一意識別子 (GUID) をキーとして使用する

System.Guid は、ジョブ処理システムで新しいジョブを作成する場合など、ランダム性とグローバル一意性が必要な場合に便利なキーを作成します。 キーの割り当てを調整する必要はありません。これを行うと、システムに単一障害点ができたり、ボトルネックになる場合がある、リソースに対するシステム側ロックが発生したりする可能性があります。 GUID が衝突する可能性は非常に低いので、ランダムな識別子を割り当てる必要があるシステムを構築する際によく使用されます。

クライアント コードでの GUID によるグレインの参照:

var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());

グレイン コードからの主キーの取得:

public override Task OnActivateAsync()
{
    Guid primaryKey = this.GetPrimaryKey();
    return base.OnActivateAsync();
}

キーとして整数を使用

長整数も使用できます。これは、GUID より数値インデックスが優先される、グレインがリレーショナル データベースに永続化される場合に意味があります。

クライアント コードでの長整数によるグレインの参照:

var grain = grainFactory.GetGrain<IExample>(1);

グレイン コードからの主キーの取得:

public override Task OnActivateAsync()
{
    long primaryKey = this.GetPrimaryKeyLong();
    return base.OnActivateAsync();
}

キーとして文字列を使用

文字列も使用できます。

クライアント コードでの文字列によるグレインの参照:

var grain = grainFactory.GetGrain<IExample>("myGrainKey");

グレイン コードからの主キーの取得:

public override Task OnActivateAsync()
{
    string primaryKey = this.GetPrimaryKeyString();
    return base.OnActivateAsync();
}

複合キーの使用

GUID または long が適していないシステムの場合は、複合主キーを選択できます。これにより、GUID または long と文字列の組み合わせを使ってグレインを参照できます。

インターフェイスでは、次のように、IGrainWithGuidCompoundKey または IGrainWithIntegerCompoundKey インターフェイスを継承できます。

public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
    Task Hello();
}

クライアント コードでは、これによりグレインのファクトリの IGrainFactory.GetGrain メソッドに 2 番目の引数が追加されます。

var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);

グレインで複合キーにアクセスするには、GrainExtensions.GetPrimaryKey メソッドのオーバーロード (GrainExtensions.GetPrimaryKeyLong) を呼び出すことができます。

public class ExampleGrain : Orleans.Grain, IExampleGrain
{
    public Task Hello()
    {
        long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
        Console.WriteLine($"Hello from {keyExtension}");

        Task.CompletedTask;
    }
}

グレインが論理識別子を使用する理由

.NET などのオブジェクト指向環境では、オブジェクトの ID は、オブジェクトへの参照と区別するのが困難です。 new キーワードを使用してオブジェクトが作成されると、返される参照は、そのオブジェクトを表す外部エンティティにオブジェクトをマップするものを除き、その ID のすべての側面を表します。 Orleans は分散システム用に設計されています。 分散システムでは、オブジェクト参照は単一プロセスのアドレス空間に制限されるため、オブジェクト参照はインスタンス ID を表すことができません。 Orleans では、この制限を回避するために論理識別子が使用されます。 グレインは論理識別子を使用して、グレイン参照がプロセスの有効期間を越えて有効であり、あるプロセスから別のプロセスに移植可能であるため、参照が作成された同じエンティティを参照しながら、それらを格納して後で取得したり、ネットワーク経由でアプリケーション内の別のプロセスに送信したりできます。