Servicios de grano

Los servicios de grano son servicios con particiones a los que se puede acceder de forma remota para admitir los granos de funcionalidad. Cada instancia de un servicio de granos es responsable de algún conjunto de granos y esos granos pueden obtener una referencia al servicio de granos que actualmente es responsable de su mantenimiento mediante un GrainServiceClient.

Los servicios de grano existen para admitir casos en los que se debe distribuir la responsabilidad de los granos de mantenimiento alrededor del clúster de Orleans. Por ejemplo, los recordatorios de Orleans se implementan mediante servicios de grano: cada silo es responsable de controlar las operaciones de recordatorio para un subconjunto de granos y notificar a esos granos cuando se activen sus recordatorios.

Los servicios de grano se configuran en silos y se inicializan cuando se inicia el silo, antes de que el silo finalice la inicialización. No se recopilan cuando están inactivos y, en su lugar, tienen duraciones que se extienden durante la vigencia del propio silo.

Creación de un GrainService

Un GrainService es un intervalo de agregación especial; uno que no tiene identidad estable y se ejecuta en cada silo desde el inicio hasta el apagado. Hay varios pasos implicados al implementar una interfaz IGrainService.

  1. Defina la interfaz de comunicación de servicio específica. La interfaz deGrainService se crea con los mismos principios que usaría para crear la interfaz de un intervalo de agregación.

    public interface IDataService : IGrainService
    {
        Task MyMethod();
    }
    
  2. Cree el DataService servicio de grano. Es bueno saber que también puedes insertar un IGrainFactory para poder realizar llamadas de intervalo de agregación específicas desde tu 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. Crea una interfaz para que otros granos usen GrainServiceClient<TGrainService>GrainServiceClient para conectarse a GrainService.

    public interface IDataServiceClient : IGrainServiceClient<IDataService>, IDataService
    {
    }
    
  1. Cree el cliente de servicio de grano. Normalmente, los clientes actúan como servidores proxy para los servicios específicos a los que se dirigen, por lo que normalmente agregará un método para cada método en el servicio de destino. Estos métodos tendrán que obtener una referencia al servicio de grano que tienen como destino para que puedan llamar a él. La clase base GrainServiceClient<T> proporciona varias sobrecargas del método GetGrainService que pueden devolver una referencia de grano correspondiente a GrainId, un hash numérico (uint) o un SiloAddress. Las dos últimas sobrecargas son para los casos avanzados en los que un desarrollador quiere usar un mecanismo diferente para asignar la responsabilidad a los hosts o quiere abordar un host directamente. En el código de ejemplo siguiente, definimos una propiedad GrainServiceque devuelve para IDataService el grano que llama a DataServiceClient. Para ello, usamos la GetGrainService(GrainId) sobrecarga junto con la CurrentGrainReference propiedad .

    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. Crea el cliente de servicio de intervalo de agregación real. Actúa prácticamente como un proxy para el servicio de datos. Desafortunadamente, tienes que escribir manualmente todas las asignaciones de métodos, que son solo simples de líneas únicas.

    public class DataServiceClient : GrainServiceClient<IDataService>, IDataServiceClient
    {
        public DataServiceClient(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }
    
        public Task MyMethod() => GrainService.MyMethod();
    }
    
  1. Inserta el cliente de servicio de intervalo de agregación en los otros intervalos que lo necesitan. No se garantiza que GrainServiceClient acceda a GrainService en el silo local. Tu comando podría enviarse a GrainService en cualquier silo del clúster.

    public class MyNormalGrain: Grain<NormalGrainState>, INormalGrain
    {
        readonly IDataServiceClient _dataServiceClient;
    
        public MyNormalGrain(
            IGrainActivationContext grainActivationContext,
            IDataServiceClient dataServiceClient) =>
                _dataServiceClient = dataServiceClient;
    }
    
  2. Configure el servicio de grano y el cliente del servicio de grano en el silo. Debes hacerlo para que el silo inicie el GrainService.

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

Notas adicionales

Hay un método de extensión en el GrainServicesSiloBuilderExtensions.AddGrainService que se usa para registrar servicios específicos.

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

El silo captura los tipos IGrainService del proveedor de servicios al iniciar orleans/src/Orleans.Runtime/Silo/Silo.cs.

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

Se debe hacer referencia al paquete NuGet Microsoft.Orleans.Runtime en el proyecto GrainService.

Se debe hacer referencia al paquete NuGet Microsoft.Orleans.OrleansRuntime en el proyecto GrainService.

Para que esto funcione, debes registrar tanto el servicio como su cliente. La salida tendrá un aspecto similar a este:

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