Compartir a través de


Orleans Información general sobre el ciclo de vida del silo

Orleans los silos utilizan un ciclo de vida observable para el arranque ordenado y la desconexión de los sistemas y los componentes de la capa de Orleans aplicación. Para obtener más información sobre los detalles de implementación, consulte Orleans ciclo de vida.

Etapas

Orleans Los clientes de silo y clúster usan un conjunto común de fases del ciclo de vida del servicio:

public static class ServiceLifecycleStage
{
    public const int First = int.MinValue;
    public const int RuntimeInitialize = 2_000;
    public const int RuntimeServices = 4_000;
    public const int RuntimeStorageServices = 6_000;
    public const int RuntimeGrainServices = 8_000;
    public const int ApplicationServices = 10_000;
    public const int BecomeActive = Active - 1;
    public const int Active = 20_000;
    public const int Last = int.MaxValue;
}

Registro

Debido al concepto de inversión de control, donde los participantes se integran en el ciclo de vida en vez de que este contenga un conjunto centralizado de pasos de inicialización, no siempre queda claro en el código cuál es el orden de inicio y apagado. Para ayudar a solucionar esto, Orleans agrega el registro antes del inicio del silo para informar de qué componentes participan en cada fase. Estos registros se registran en el nivel de registro de información del Orleans.Runtime.SiloLifecycleSubject registrador. Por ejemplo:

Information, Orleans.Runtime.SiloLifecycleSubject, "Stage 2000: Orleans.Statistics.PerfCounterEnvironmentStatistics, Orleans.Runtime.InsideRuntimeClient, Orleans.Runtime.Silo"

Information, Orleans.Runtime.SiloLifecycleSubject, "Stage 4000: Orleans.Runtime.Silo"

Information, Orleans.Runtime.SiloLifecycleSubject, "Stage 10000: Orleans.Runtime.Versions.GrainVersionStore, Orleans.Storage.AzureTableGrainStorage-Default, Orleans.Storage.AzureTableGrainStorage-PubSubStore"

Además, Orleans registra de forma similar la información de tiempo y error de cada componente por fase. Por ejemplo:

Information, Orleans.Runtime.SiloLifecycleSubject, "Lifecycle observer Orleans.Runtime.InsideRuntimeClient started in stage 2000 which took 33 Milliseconds."

Information, Orleans.Runtime.SiloLifecycleSubject, "Lifecycle observer Orleans.Statistics.PerfCounterEnvironmentStatistics started in stage 2000 which took 17 Milliseconds."

Participación en el ciclo de vida de silo

La lógica de la aplicación puede participar en el ciclo de vida del silo registrando un servicio participante en el contenedor de servicios del silo. Registre el servicio como ILifecycleParticipant<TLifecycleObservable>, donde T es ISiloLifecycle.

public interface ISiloLifecycle : ILifecycleObservable
{
}

public interface ILifecycleParticipant<TLifecycleObservable>
    where TLifecycleObservable : ILifecycleObservable
{
    void Participate(TLifecycleObservable lifecycle);
}

Cuando comienza silo, todos los participantes (ILifecycleParticipant<ISiloLifecycle>) del contenedor pueden participar si se activa su comportamiento ILifecycleParticipant<TLifecycleObservable>.Participate. Una vez que todos han tenido la oportunidad de participar, el ciclo de vida observable del silo inicia todas las fases en orden.

Ejemplo

Con la introducción del ciclo de vida del silo, los proveedores de arranque, que anteriormente le permitían insertar lógica en la fase de inicialización del proveedor, ya no son necesarios. Ahora puede incorporar lógica de aplicación en cualquier fase del inicio del silo. No obstante, agregamos una fachada de "tarea de inicio" para facilitar la transición a los desarrolladores que utilizaban proveedores bootstrap. Como ejemplo de cómo puede desarrollar componentes que participan en el ciclo de vida del silo, veamos la interfaz de la tarea de inicio.

La tarea de inicio solo debe heredar de ILifecycleParticipant<ISiloLifecycle> y suscribir la lógica de la aplicación al ciclo de vida del silo en la fase especificada.

class StartupTask : ILifecycleParticipant<ISiloLifecycle>
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Func<IServiceProvider, CancellationToken, Task> _startupTask;
    private readonly int _stage;

    public StartupTask(
        IServiceProvider serviceProvider,
        Func<IServiceProvider, CancellationToken, Task> startupTask,
        int stage)
    {
        _serviceProvider = serviceProvider;
        _startupTask = startupTask;
        _stage = stage;
    }

    public void Participate(ISiloLifecycle lifecycle)
    {
        lifecycle.Subscribe<StartupTask>(
            _stage,
            cancellation => _startupTask(_serviceProvider, cancellation));
    }
}

Desde la implementación anterior, puede ver que, en la Participate(...) llamada, se suscribe al ciclo de vida del silo en la fase configurada, pasando la devolución de llamada de la aplicación en lugar de su lógica de inicialización. Los componentes que necesitan inicialización en una fase determinada proporcionarían su devolución de llamada, pero el patrón sigue siendo el mismo. Ahora que tiene un StartupTask que asegura que se llama al hook de la aplicación en la fase configurada, debe asegurarse de que el StartupTask participe en el ciclo de vida del silo.

Para ello, solo tiene que registrarlo en el contenedor. Para ello, use una función de extensión en ISiloHostBuilder:

public static ISiloHostBuilder AddStartupTask(
    this ISiloHostBuilder builder,
    Func<IServiceProvider, CancellationToken, Task> startupTask,
    int stage = ServiceLifecycleStage.Active)
{
    builder.ConfigureServices(services =>
        services.AddTransient<ILifecycleParticipant<ISiloLifecycle>>(
            serviceProvider =>
                new StartupTask(
                    serviceProvider, startupTask, stage)));

    return builder;
}

Al registrar el StartupTask en el contenedor de servicios del silo como la interfaz de marcador ILifecycleParticipant<ISiloLifecycle>, se indica al silo que este componente necesita participar en el ciclo de vida del silo.