Службы зерна

Службы зерна являются удаленными, секционированными службами для поддержки функциональных возможностей. Каждый экземпляр службы зерна отвечает за некоторый набор зерна, и эти зерна могут получить ссылку на службу зерна, которая в настоящее время отвечает за обслуживание их с помощью GrainServiceClient.

Службы зерна существуют для поддержки случаев, когда ответственность за обслуживание зерна должна распространяться по всему кластеру Orleans . Например, Orleans напоминания реализуются с помощью служб зерна: каждый сило отвечает за обработку операций напоминания за подмножество зерна и уведомляет эти зерна при срабатывании напоминаний.

Службы зерна настраиваются на оси и инициализируются при запуске silo, прежде чем silo завершит инициализацию. Они не собираются при бездействии и вместо этого имеют время существования, которые расширяются в течение всего времени существования самого сило.

Создание службы GrainService

Это GrainService специальное зерно; тот, который не имеет стабильного удостоверения, и работает в каждом сило от запуска до завершения работы. При реализации 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. Создайте интерфейс для использования другими зернами для GrainServiceClient<TGrainService>GrainServiceClient подключения к нему GrainService.

    public interface IDataServiceClient : IGrainServiceClient<IDataService>, IDataService
    {
    }
    
  1. Создайте клиент службы зерна. Клиенты обычно используют прокси-серверы для целевых служб, поэтому обычно добавляется метод для каждого метода в целевой службе. Эти методы должны получить ссылку на службу зерна, на которую они нацелены, чтобы они могли вызывать ее. Базовый GrainServiceClient<T> класс предоставляет несколько перегрузок GetGrainService метода, который может возвращать ссылку на зерно, соответствующую GrainIdчисловому хэшу (uint) или a SiloAddress. Последние две перегрузки предназначены для сложных случаев, когда разработчик хочет использовать другой механизм для сопоставления ответственности с узлами или хочет напрямую обратиться к узлу. В приведенном ниже примере кода мы определим свойство, GrainServiceкоторое возвращает IDataService для зерна, вызывающего объект DataServiceClient. Для этого мы используем перегрузку 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. Настройте клиент службы зерна и службы зерна в silo. Это необходимо сделать так, чтобы сило начнется GrainService.

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

Дополнительные примечания

Существует метод расширения, в GrainServicesSiloBuilderExtensions.AddGrainService котором используется для регистрации служб зерна.

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

При запуске silo извлекает IGrainService типы из поставщика услуг: orleans/src/Orleans. Среда выполнения/Silo/Silo.cs

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

Microsoft .Orleans. Пакет NuGet среды выполнения должен ссылаться на GrainService проект.

Microsoft .Orleans.OrleansПакет NuGet среды выполнения должен ссылаться на GrainService проект.

Чтобы это работало, необходимо зарегистрировать как службу, так и его клиент. Код выглядит примерно так:

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