次の方法で共有


グレイン サービス

グレイン サービスは、グレインの機能をサポートするための、リモートでアクセスできるパーティション分割されたサービスです。 グレイン サービスの各インスタンスはあるグレインのセットを担当し、それらのグレインは、GrainServiceClient を使用して、現在それらのサービス提供を担当しているグレイン サービスへの参照を取得できます。

グレイン サービスは、グレインのサービスの提供を Orleans クラスターで分散する必要があるケースをサポートするために存在します。 たとえば、Orleans Reminders はグレイン サービスを使用して実装されます。各サイロが、グレインのサブセットに対するリマインダー操作の処理と、リマインダーが発生したときのそれらのグレインへの通知を担当します。

グレイン サービスはサイロ上に構成され、サイロが開始されると、サイロの初期化が完了する前に初期化されます。 それらはアイドル状態のときは収集されず、代わりにサイロ自体の有効期間まで延長される有効期間を持ちます。

GrainService を作成する

GrainService は特別なグレインで、安定した ID を持たず、起動からシャットダウンまですべてのサイロで実行されます。 IGrainService インターフェイスを実装するときには、いくつかの重大な手順が必要です。

  1. グレイン サービスの通信インターフェイスを定義します。 GrainService のインターフェイスは、グレインのインターフェイスを作成するときと同じ原則で作成します。

    public interface IDataService : IGrainService
    {
        Task MyMethod();
    }
    
  2. DataService グレイン サービスを作成します。 また、IGrainFactory を挿入することで、GrainService からグレイン呼び出しを行うことができることを知っておくと便利です。

    [Reentrant]
    public class DataService : GrainService, IDataService
    {
        readonly IGrainFactory _grainFactory;
    
        public DataService(
            IServiceProvider services,
            GrainId id,
            Silo silo,
            ILoggerFactory loggerFactory,
            IGrainFactory grainFactory)
            : base(id, silo, loggerFactory)
        {
            _grainFactory = grainFactory;
        }
    
        public override Task Init(IServiceProvider serviceProvider) =>
            base.Init(serviceProvider);
    
        public override Task Start() => base.Start();
    
        public override Task Stop() => base.Stop();
    
        public Task MyMethod()
        {
            // TODO: custom logic here.
            return Task.CompletedTask;
        }
    }
    
    [Reentrant]
    public class DataService : GrainService, IDataService
    {
        readonly IGrainFactory _grainFactory;
    
        public DataService(
            IServiceProvider services,
            IGrainIdentity id,
            Silo silo,
            ILoggerFactory loggerFactory,
            IGrainFactory grainFactory)
            : base(id, silo, loggerFactory)
        {
            _grainFactory = grainFactory;
        }
    
        public override Task Init(IServiceProvider serviceProvider) =>
            base.Init(serviceProvider);
    
        public override Task Start() => base.Start();
    
        public override Task Stop() => base.Stop();
    
        public Task MyMethod()
        {
            // TODO: custom logic here.
            return Task.CompletedTask;
        }
    }
    
  3. 他のグレインが GrainService に接続するために使う GrainServiceClient<TGrainService>GrainServiceClient のインターフェイスを作成します。

    public interface IDataServiceClient : IGrainServiceClient<IDataService>, IDataService
    {
    }
    
  1. グレイン サービスのクライアントを作成します。 一般に、クライアントは対象となるグレイン サービスのプロキシとして機能するため、通常は対象のサービスのメソッドごとにメソッドを追加します。 これらのメソッドは、対象となるグレイン サービスへの参照を取得し、それを呼び出すことができるようにする必要があります。 GrainServiceClient<T> 基底クラスは、GrainId、数値ハッシュ (uint)、または SiloAddress に対応するグレイン参照を返すことができる、GetGrainService メソッドのいくつかのオーバーロードを提供します。 後の 2 つのオーバーロードは、開発者が別のメカニズムを使用してホストに責任をマップしたり、ホストに直接対処したりする高度なケース用です。 次のサンプル コードでは、DataServiceClient を呼び出しているグレインの IDataService を返す、プロパティ GrainService を定義しています。 これを行うために、GetGrainService(GrainId) オーバーロードを CurrentGrainReference プロパティと組み合わせて使用しています。

    public class DataServiceClient : GrainServiceClient<IDataService>, IDataServiceClient
    {
        public DataServiceClient(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }
    
        // For convenience when implementing methods, you can define a property which gets the IDataService
        // corresponding to the grain which is calling the DataServiceClient.
        private IDataService GrainService => GetGrainService(CurrentGrainReference.GrainId);
    
        public Task MyMethod() => GrainService.MyMethod();
    }
    
  1. 実際のグレイン サービス クライアントを作成します。 これはほぼデータ サービスのプロキシとして機能するだけです。 残念ながら、すべてのメソッド マッピングを手動で入力する必要がありますが、これは単純なワンライナーだけです。

    public class DataServiceClient : GrainServiceClient<IDataService>, IDataServiceClient
    {
        public DataServiceClient(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }
    
        public Task MyMethod() => GrainService.MyMethod();
    }
    
  1. グレイン サービス クライアントを、それを必要とする他のグレインに挿入します。 GrainServiceClient はローカル サイロ上の GrainService にアクセスすることが保証されていません。 コマンドは、クラスター内のどのサイロの GrainService にも送信される可能性があります。

    public class MyNormalGrain: Grain<NormalGrainState>, INormalGrain
    {
        readonly IDataServiceClient _dataServiceClient;
    
        public MyNormalGrain(
            IGrainActivationContext grainActivationContext,
            IDataServiceClient dataServiceClient) =>
                _dataServiceClient = dataServiceClient;
    }
    
  2. サイロでグレイン サービスとグレイン サービスのクライアントを構成します。 サイロが GrainService を開始するように、これを行う必要があります。

    (ISiloHostBuilder builder) =>
        builder.ConfigureServices(
            services => services.AddGrainService<DataService>()
                                .AddSingleton<IDataServiceClient, DataServiceClient>());
    

その他のメモ

グレイン サービスを登録するために使用される GrainServicesSiloBuilderExtensions.AddGrainService の拡張メソッドがあります。

services.AddSingleton<IGrainService>(
    serviceProvider => GrainServiceFactory(grainServiceType, serviceProvider));

サイロは orleans/src/Orleans.Runtime/Silo/Silo.cs の起動時、サービス プロバイダーから IGrainService 型をフェッチします。

var grainServices = this.Services.GetServices<IGrainService>();

Microsoft.Orleans.Runtime NuGet パッケージは、GrainService プロジェクトで参照される必要があります。

Microsoft.Orleans.OrleansRuntime NuGet パッケージは、GrainService プロジェクトで参照される必要があります。

これを機能させるには、サービスとそのクライアントの両方を登録する必要があります。 コードは次のようになります。

var builder = new HostBuilder()
    .UseOrleans(c =>
    {
        c.AddGrainService<DataService>()  // Register GrainService
        .ConfigureServices(services =>
        {
            // Register Client of GrainService
            services.AddSingleton<IDataServiceClient, DataServiceClient>();
        });
    })