Übersicht über den Lebenszyklus von Orleans-Silos

Orleans-Silos verwenden einen überwachbaren Lebenszyklus für das geordnete Starten und Herunterfahren von Orleans-Systemen sowie von Komponenten auf Anwendungsebene. Weitere Informationen zu den Implementierungsdetails finden Sie unter Orleans-Lebenszyklus.

Phasen

Clients von Orleans-Silos und -Clustern verwenden gemeinsame Phasen des Dienstlebenszyklus.

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

Protokollierung

Aufgrund der Steuerungsumkehrung, bei der die teilnehmenden Komponenten dem Lebenszyklus beitreten und nicht der Lebenszyklus einen zentralisierten Satz von Initialisierungsschritten aufweist, ist im Code nicht immer klar ersichtlich, wie die Reihenfolge für das Starten/Herunterfahren ist. Aus diesem Grund wurde vor dem Silostart eine Protokollierung hinzugefügt, die darüber informiert, welche Komponenten in den einzelnen Phasen teilnehmen. Diese Protokolle werden auf der Protokollebene Information in der Protokollierung Orleans.Runtime.SiloLifecycleSubject erfasst. Zum Beispiel:

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"

Darüber hinaus werden auch für jede Komponente Zeit- und Fehlerinformationen pro Phase protokolliert. Zum Beispiel:

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

Teilnahme am Lebenszyklus des Silos

Die Anwendungslogik kann am Lebenszyklus des Silos teilnehmen, indem sie einen teilnehmenden Dienst im Dienstcontainer des Silos registriert. Der Dienst muss als ILifecycleParticipant<TLifecycleObservable> registriert werden, wobei T gleich ISiloLifecycle ist.

public interface ISiloLifecycle : ILifecycleObservable
{
}

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

Wenn das Silo gestartet wird, müssen alle Teilnehmer (ILifecycleParticipant<ISiloLifecycle>) im Container das Verhalten ILifecycleParticipant<TLifecycleObservable>.Participate aufrufen, um teilnehmen zu können. Sobald alle Dienste die Möglichkeit zur Teilnahme hatten, startet der überwachbare Lebenszyklus des Silos alle Phasen der Reihe nach.

Beispiel

Mit der Einführung des Silolebenszyklus werden Bootstrapanbieter zum Einfügen von Logik in der Initialisierungsphase des Anbieters überflüssig, da Anwendungslogik jetzt in jeder Phase des Silostarts eingefügt werden kann. Damit sich Entwickler*innen, die bisher Bootstrapanbieter genutzt haben, besser zurechtfinden, wurde dennoch eine Starttask-Oberfläche hinzugefügt. Als Beispiel für die Entwicklung von Komponenten, die am Lebenszyklus des Silos teilnehmen, soll die Oberfläche für den Starttask dienen.

Der Starttask muss nur von ILifecycleParticipant<ISiloLifecycle> erben und die Anwendungslogik in der angegebenen Phase des Silolebenszyklus abonnieren.

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

Die obige Implementierung zeigt, dass im Aufruf Participate(...) der Silolebenszyklus in der konfigurierten Phase abonniert und der Anwendungsrückruf anstelle der Initialisierungslogik übergeben wird. Komponenten, die in einer bestimmten Phase initialisiert werden müssen, stellen hier ihren Rückruf bereit, aber das Muster ist das gleiche. Nachdem StartupTask nun sicherstellt, dass der Anwendungshook in der konfigurierten Phase aufgerufen wird, müssen Sie nun sicherstellen, dass StartupTask am Lebenszyklus des Silos teilnimmt.

Dazu müssen Sie den Starttask nur im Container registrieren. Verwenden Sie hierzu eine Erweiterungsfunktion für 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;
}

Durch die Registrierung von „StartupTask“ im Dienstcontainer des Silos als Markerschnittstelle ILifecycleParticipant<ISiloLifecycle> wird das Silo darüber informiert, dass diese Komponente am Lebenszyklus des Silos teilnehmen soll.