Orleans 2.0 官方文档 —— 4.4 Grains -> 定时器和提醒器(Reminder)

定时器和提醒器(Reminder)

Orleans运行时提供了两种机制,称为定时器和提醒器,使开发人员能够指定grain的周期性行为。

计时器

定时器说明

定时器用于创建不需要跨越多个激活(grain的实例化)的周期性的grain的行为。它基本上等同于标准的.NET System.Threading.Timer类。此外,在它运行的grain激活中,它还受单线程执行保证的约束。

每个激活都可以具有与其相关联的零个或多个计时器。在与定时器关联激活的运行时上下文中,运行时执行每个定时器的例程。

定时器使用

要启动定时器,请使用Grain.RegisterTimer方法,该方法返回 IDisposable引用:

public IDisposable RegisterTimer(
       Func<object, Task> asyncCallback, // function invoked when the timer ticks
       object state,                     // object tp pass to asyncCallback
       TimeSpan dueTime,                 // time to wait before the first timer tick
       TimeSpan period)                  // the period of the timer

通过disposing可以取消计时器。

如果激活被禁用,或者发生故障且其silo崩溃,则定时器将停止触发。

重要的考量因素

  • 启用激活收集后,执行定时器回调,不会将激活状态从Idle状态更改为In Use状态。这意味着计时器不能用于延迟停用其他空闲的激活体。
  • 传递给Grain.RegisterTimer的时间段是,从完成asyncCallback返回的Task起,到下一次调用asyncCallback时所经过的时间。这不仅使得对asyncCallback的连续调用不可能重叠,而且还使得asyncCallback完成所需的时长,影响了调用asyncCallback的频率。这是与System.Threading.Timer的语义的重要偏离。
  • 每次调用asyncCallback,都会在单独的一个回合上传递给一个激活,并且永远不会与同一激活的其他回合同时运行。但请注意,asyncCallback的调用不是作为消息传递的,因此不受消息交错语义的影响。这意味着应该认为asyncCallback的调用行为,就像在相对于该粒度的其他消息的可重入的grain上运行一样。

提醒器

提醒器说明

提醒与定时器类似,但有一些重要区别:

  • 除非明确取消,否则提醒器是持久性的,并且将在几乎所有情况下(包括部分或全部的群集重新启动)继续触发。
  • 提醒器的“定义”被写入存储。然而,每一个特定的事件,及其特定的时间,却不是如此。这具有副作用,即如果集群刚好在提醒器到点时完全关闭,则将错过此次到点,仅在提醒器的下一个到点时发生。
  • 提醒器与一个grain关联,而不与任何特定的激活关联。
  • 如果grain没有与之关联的激活,并且提醒器到点,则将创建一个grain。例如:如果激活变为空闲且被停用,则与相同grain相关联的提醒器,将在下一个到点时重新激活grain。
  • 提醒器通过消息传递,并且与所有其他grain方法具有相同的交错语义。
  • 提醒器不应用于高频计时器 - 它们的周期应以分钟,小时或天来衡量。

配置

提醒器是持久化的,依赖于存储来发挥作用。您必须在提醒器的子系统运行之前,指定要使用的存储后备。这是通过扩展方法UseXReminderService,来配置其中提醒器的提供程序之一而完成的,其中X是提供程序的名称,例如UseAzureTableReminderService

Azure表配置:

// TODO replace with your connection string
const string connectionString = "YOUR_CONNECTION_STRING_HERE";
var silo = new SiloHostBuilder()
    [...]
    .UseAzureTableReminderService(options => options.ConnectionString = connectionString)
    [...]

SQL:

// TODO replace with your connection string
const string connectionString = "YOUR_CONNECTION_STRING_HERE";
const string invariant = "YOUR_INVARIANT";
var silo = new SiloHostBuilder()
    [...]
    .UseAdoNetReminderService(options => 
    {
        options.ConnectionString = connectionString;
        options.Invariant = invariant;
    })
    [...]

如果您只是想要一个实现提醒器的占位符,而不需设置Azure帐户或SQL数据库,那么这将为您提供一个仅限开发的提醒器系统实现:

var silo = new SiloHostBuilder()
    [...]
    .UseInMemoryReminderService()
    [...]

提醒器用法

使用提醒器的grain,必须实现IRemindable.RecieveReminder方法。

Task IRemindable.ReceiveReminder(string reminderName, TickStatus status)
{
    Console.WriteLine("Thanks for reminding me-- I almost forgot!");
    return TaskDone.Done;
}

要启动提醒器,请使用Grain.RegisterOrUpdateReminder方法,该方法返回IOrleansReminder对象:

protected Task RegisterOrUpdateReminder(string reminderName, TimeSpan dueTime, TimeSpan period)
  • reminderName是一个字符串,必须唯一地标识上下文的grain作用域内的提醒器。
  • dueTime指定在发出第一个定时器到点之前等待的时间量。
  • period指定定时器的周期。

由于提醒器在任何单次激活的生命周期中都存在,因此必须显式地取消它们(而不是被dispose)。您通过调用Grain.UnregisterReminder取消提醒器:

protected Task UnregisterReminder(IOrleansReminder reminder)

提醒器是Grain.RegisterOrUpdateReminder返回的句柄对象。

IOrleansReminder的实例不保证在激活的生命周期之外是有效的如果要以持续的方式标识提醒,请使用包含提醒器名称的字符串。

如果您只有提醒器名称,并且需要相应的IOrleansReminder实例 ,请调用Grain.GetReminder方法:

protected Task GetReminder(string reminderName)

我应该使用哪种?

我们建议您在以下情况下使用定时器:

  • 如果激活被停用或发生故障,定时器停止工作,这种情况并不要紧(或者是预期内的)。
  • 定时器的分辨率很小(例如,合理地表示成几秒或几分钟)。
  • 定时器回调可以从Grain.OnActivateAsync启动,也可以在调用一个grain方法时启动。

我们建议您在以下情况下使用提醒器:

  • 当周期性行为需要在激活和任何失败时均能执行。
  • 执行不频繁的任务(例如,在几分钟,几小时或几天内合理表达)。

结合计时器和提醒

您可以考虑使用提醒器和计时器的组合来实现目的。例如,如果您需要一个在激活过程中存活下来且分辨率很小的定时器,可以使用每五分钟运行一次的提醒器,其目的是唤醒一个grain重新启动一个本地的定时器,而该定时器可能由于已停用而发生丢失。

你可能感兴趣的:(Orleans)