silo的生命周期
概述
Orleans silo使用可观察的生命周期(参见Orleans Lifecycle),来有序地启动和关闭Orleans系统以及应用程序层组件。
阶段
Orleans Silo和Cluster Client使用一组通用的服务生命周期阶段。
public static class ServiceLifecycleStage
{
public const int First = int.MinValue;
public const int RuntimeInitialize = 2000;
public const int RuntimeServices = 4000;
public const int RuntimeStorageServices = 6000;
public const int RuntimeGrainServices = 8000;
public const int ApplicationServices = 10000;
public const int BecomeActive = Active-1;
public const int Active = 20000;
public const int Last = int.MaxValue;
}
- First - 服务生命周期的第一阶段
- RuntimeInitialize - 初始化运行时的环境。Silo初始化线程。
- RuntimeServices - 启动运行时服务。Silo初始化网络和各种代理。
- RuntimeStorageServices - 初始化运行时存储。
- RuntimeGrainServices - 启动用于grain的运行时服务。这包括grain类型管理,成员服务和grain目录。
- ApplicationServices - 应用程序层服务。
- BecomeActive - Silo加入了集群。
- Active - Silo在集群中处于活动状态,随时可以接受工作负载。
- Last - 服务生命周期的最后阶段
记录日志
由于控制反转中的参与者加入了生命周期,而生命周期并不具有一些集中的初始化步骤,因此代码中并不总是清楚启动/关闭的顺序是什么。为了解决这个问题,在silo启动之前添加了日志记录,以报告在每个阶段参与的组件。这些日志以Information的级别,记录在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在2000阶段开始,耗时33毫秒。” Information,Orleans.Runtime.SiloLifecycleSubject,“Lifecycle observer Orleans.Statistics.PerfCounterEnvironmentStatistics在2000阶段耗时17毫秒。“
silo的生命周期参与
应用程序逻辑可以通过在silo的服务容器中注册参与的服务,来参与silo的生命周期。该服务必须注册为ILifecycleParticipant。
public interface ISiloLifecycle : ILifecycleObservable
{
}
public interface ILifecycleParticipant<TLifecycleObservable>
where TLifecycleObservable : ILifecycleObservable
{
void Participate(TLifecycleObservable lifecycle);
}
在silo启动时,容器中的所有参与者(ILifecycleParticipant<ISiloLifecycle>
),都将有机会通过调用其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)
{
this.serviceProvider = serviceProvider;
this.startupTask = startupTask;
this.stage = stage;
}
public void Participate(ISiloLifecycle lifecycle)
{
lifecycle.Subscribe<StartupTask>(
this.stage,
cancellation => this.startupTask(this.serviceProvider, cancellation));
}
}
从上面的实现中,我们可以看到,在StartupTask的Participate(..)
调用中,它在配置阶段订阅了silo生命周期,传递应用程序的回调而不是它自己的初始化逻辑。需要在给定阶段初始化的组件将提供自己的回调,不过模式是相同的。既然我们有一个StartupTask来确保应用程序的钩子在配置阶段被调用,那么我们需要确保StartupTask参与silo的生命周期。为此,我们只需要在容器中注册它。我们使用SiloHost构建器上的扩展功能执行此操作。
public static ISiloHostBuilder AddStartupTask(
this ISiloHostBuilder builder,
Func<IServiceProvider, CancellationToken, Task> startupTask,
int stage = ServiceLifecycleStage.Active)
{
builder.ConfigureServices(services =>
services.AddTransient<ILifecycleParticipant<ISiloLifecycle>>(sp =>
new StartupTask(
sp,
startupTask,
stage)));
return builder;
}
通过在silo的服务容器中,注册StartupTask作为标记接口ILifecycleParticipant<ISiloLifecycle>
,该组件向silo发出信号,表示它需要参与silo的生命周期。