Поделиться через


Orleans Обзор жизненного цикла silo

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

ЭТАПЫ

Orleans Клиенты silo и кластера используют общий набор этапов жизненного цикла службы.

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

Ведение журнала

Из-за инверсии элемента управления, когда участники присоединяются к жизненному циклу, а не к жизненному циклу с некоторыми централизованным набором шагов инициализации, не всегда ясно из кода, что такое порядок запуска и завершения работы. Чтобы устранить эту проблему, ведение журнала было добавлено перед запуском silo, чтобы сообщить о том, какие компоненты участвуют на каждом этапе. Эти журналы записываются на уровне журнала сведений на средстве Orleans.Runtime.SiloLifecycleSubject ведения журнала. Например:

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"

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

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

Участие жизненного цикла Silo

Логика приложения может принять участие в жизненном цикле silo, зарегистрируя в контейнере службы silo участвующие службы. Служба должна быть зарегистрирована в качестве места ILifecycleParticipant<TLifecycleObservable> T.ISiloLifecycle

public interface ISiloLifecycle : ILifecycleObservable
{
}

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

При запуске silo все участники (ILifecycleParticipant<ISiloLifecycle>) в контейнере смогут участвовать, вызывая их ILifecycleParticipant<TLifecycleObservable>.Participate поведение. Как только у всех было возможность принять участие, наблюдаемый жизненный цикл silo начнет все этапы в порядке.

Пример

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

Задача запуска должна наследоваться только от ILifecycleParticipant<ISiloLifecycle> логики приложения и подписываться на жизненный цикл silo на указанном этапе.

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

В приведенной выше реализации мы видим, что при Participate(...) вызове он подписывается на жизненный цикл silo на настроенном этапе, передавая обратный вызов приложения, а не логику инициализации. Компоненты, которые должны быть инициализированы на данном этапе, предоставляют обратный вызов, но шаблон такой же. Теперь, когда у нас есть функция StartupTask , которая обеспечит вызов перехватчика приложения на настроенном этапе, необходимо убедиться, что StartupTask он участвует в жизненном цикле silo.

Для этого необходимо зарегистрировать его только в контейнере. Мы делаем это с функцией расширения для 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;
}

Зарегистрируя StartupTask в контейнере службы silo в качестве интерфейса ILifecycleParticipant<ISiloLifecycle>маркера, это сигнализирует о том, что этот компонент должен принять участие в жизненном цикле silo.