Partilhar via


Orleans Visão geral do ciclo de vida do silo

Orleans Os silos usam um ciclo de vida observável para inicialização e desligamento ordenados de sistemas, bem como componentes da camada de Orleans aplicação. Para obter mais informações sobre os detalhes da implementação, consulte Orleans ciclo de vida.

FASES

Orleans Os clientes de silo e cluster usam um conjunto comum de estágios do ciclo de vida do serviço.

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;
}

Registo

Devido à inversão do controle, em que os participantes se juntam ao ciclo de vida em vez de o ciclo de vida ter algum conjunto centralizado de etapas de inicialização, nem sempre fica claro no código qual é a ordem de inicialização/desligamento. Para ajudar a resolver isso, o registro em log foi adicionado antes da inicialização do silo para relatar quais componentes estão participando em cada estágio. Esses logs são registrados no nível de log de informações no Orleans.Runtime.SiloLifecycleSubject registrador. Por exemplo:

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"

Além disso, as informações de tempo e erro são registradas de forma semelhante para cada componente por estágio. Por exemplo:

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."

Participação no ciclo de vida do silo

A lógica do aplicativo pode participar do ciclo de vida do silo registrando um serviço participante no contêiner de serviço do silo. O serviço deve ser registrado como um ILifecycleParticipant<TLifecycleObservable> onde T é um ISiloLifecyclearquivo .

public interface ISiloLifecycle : ILifecycleObservable
{
}

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

Quando o silo for iniciado, todos os participantes (ILifecycleParticipant<ISiloLifecycle>) no contêiner poderão participar chamando seu ILifecycleParticipant<TLifecycleObservable>.Participate comportamento. Uma vez que todos tenham tido a oportunidade de participar, o ciclo de vida observável do silo iniciará todas as etapas em ordem.

Exemplo

Com a introdução do ciclo de vida do silo, os provedores de bootstrap, que costumavam permitir que os desenvolvedores de aplicativos injetassem lógica na fase de inicialização do provedor, não são mais necessários, uma vez que a lógica do aplicativo agora pode ser injetada em qualquer estágio da inicialização do silo. No entanto, adicionamos uma fachada de "tarefa de inicialização" para ajudar na transição para desenvolvedores que estavam usando provedores de bootstrap. Como exemplo de como podem ser desenvolvidos componentes que participam do ciclo de vida do silo, veremos a fachada da tarefa de inicialização.

A tarefa de inicialização precisa apenas herdar e inscrever a lógica do ILifecycleParticipant<ISiloLifecycle> aplicativo no ciclo de vida do silo no estágio especificado.

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));
    }
}

A partir da implementação acima, podemos ver que na Participate(...) chamada ele assina o ciclo de vida do silo no estágio configurado, passando o retorno de chamada do aplicativo em vez de sua lógica de inicialização. Os componentes que precisam ser inicializados em um determinado estágio forneceriam seu retorno de chamada, mas o padrão é o mesmo. Agora que temos um StartupTask que garantirá que o gancho do aplicativo seja chamado no estágio configurado, precisamos garantir que o StartupTask participe do ciclo de vida do silo.

Para isso, basta registá-lo no contentor. Fazemos isso com uma função de extensão no 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;
}

Ao registrar o StartupTask no contêiner de serviço do silo como a interface ILifecycleParticipant<ISiloLifecycle>do marcador , isso sinaliza para o silo que esse componente precisa participar do ciclo de vida do silo.