执行组件计时器和提醒Actor timers and reminders

执行组件可通过注册计时器或提醒来计划自身的定期工作。Actors can schedule periodic work on themselves by registering either timers or reminders. 本文演示如何使用计时器和提醒,并说明它们之间的差异。This article shows how to use timers and reminders and explains the differences between them.

执行组件计时器Actor timers

执行组件计时器围绕 .NET 或 Java 计时器提供一个简单包装器,确保回叫方法采用 Actors 运行时提供的基于轮次的并发保证。Actor timers provide a simple wrapper around a .NET or Java timer to ensure that the callback methods respect the turn-based concurrency guarantees that the Actors runtime provides.

执行组件可以对其基类使用 RegisterTimer(C#) 或 registerTimer(Java) 和 UnregisterTimer(C#) 或 unregisterTimer(Java) 方法以注册和注销其计时器。Actors can use the RegisterTimer(C#) or registerTimer(Java) and UnregisterTimer(C#) or unregisterTimer(Java) methods on their base class to register and unregister their timers. 下面的示例演示了如何使用计时器 API。The example below shows the use of timer APIs. 这些 API 非常类似于 .NET 计时器或 Java 计时器。The APIs are very similar to the .NET timer or Java timer. 在此示例中,当计时器到期时,Actors 运行时会调用 MoveObject(C#) 或 moveObject(Java) 方法。In this example, when the timer is due, the Actors runtime will call the MoveObject(C#) or moveObject(Java) method. 该方法可保证遵循基于轮次的并发。The method is guaranteed to respect the turn-based concurrency. 这意味着,任何其他执行组件方法或计时器/提醒回调会一直进行,直到此回调完成执行为止。This means that no other actor methods or timer/reminder callbacks will be in progress until this callback completes execution.

class VisualObjectActor : Actor, IVisualObject
{
    private IActorTimer _updateTimer;

    public VisualObjectActor(ActorService actorService, ActorId actorId)
        : base(actorService, actorId)
    {
    }

    protected override Task OnActivateAsync()
    {
        ...

        _updateTimer = RegisterTimer(
            MoveObject,                     // Callback method
            null,                           // Parameter to pass to the callback method
            TimeSpan.FromMilliseconds(15),  // Amount of time to delay before the callback is invoked
            TimeSpan.FromMilliseconds(15)); // Time interval between invocations of the callback method

        return base.OnActivateAsync();
    }

    protected override Task OnDeactivateAsync()
    {
        if (_updateTimer != null)
        {
            UnregisterTimer(_updateTimer);
        }

        return base.OnDeactivateAsync();
    }

    private Task MoveObject(object state)
    {
        ...
        return Task.FromResult(true);
    }
}
public class VisualObjectActorImpl extends FabricActor implements VisualObjectActor
{
    private ActorTimer updateTimer;

    public VisualObjectActorImpl(FabricActorService actorService, ActorId actorId)
    {
        super(actorService, actorId);
    }

    @Override
    protected CompletableFuture onActivateAsync()
    {
        ...

        return this.stateManager()
                .getOrAddStateAsync(
                        stateName,
                        VisualObject.createRandom(
                                this.getId().toString(),
                                new Random(this.getId().toString().hashCode())))
                .thenApply((r) -> {
                    this.registerTimer(
                            (o) -> this.moveObject(o),                        // Callback method
                            "moveObject",
                            null,                                             // Parameter to pass to the callback method
                            Duration.ofMillis(10),                            // Amount of time to delay before the callback is invoked
                            Duration.ofMillis(timerIntervalInMilliSeconds));  // Time interval between invocations of the callback method
                    return null;
                });
    }

    @Override
    protected CompletableFuture onDeactivateAsync()
    {
        if (updateTimer != null)
        {
            unregisterTimer(updateTimer);
        }

        return super.onDeactivateAsync();
    }

    private CompletableFuture moveObject(Object state)
    {
        ...
        return this.stateManager().getStateAsync(this.stateName).thenCompose(v -> {
            VisualObject v1 = (VisualObject)v;
            v1.move();
            return (CompletableFuture<?>)this.stateManager().setStateAsync(stateName, v1).
                    thenApply(r -> {
                      ...
                      return null;});
        });
    }
}

计时器的下一个周期在回调完成执行之后启动。The next period of the timer starts after the callback completes execution. 这意味着计时器会在回调执行期间停止,并在回调完成时启动。This implies that the timer is stopped while the callback is executing and is started when the callback finishes.

执行组件运行时会保存回叫完成时对执行组件的状态管理器所做的更改。The Actors runtime saves changes made to the actor's State Manager when the callback finishes. 如果保存状态时发生错误,则会停用该执行组件对象并激活一个新实例。If an error occurs in saving the state, that actor object will be deactivated and a new instance will be activated.

如果在垃圾回收过程中停用了执行组件,所有计时器都会停止。All timers are stopped when the actor is deactivated as part of garbage collection. 此后不会调用任何计时器回调。No timer callbacks are invoked after that. 此外,执行组件运行时不保留有关在停用之前运行的计时器的任何信息。Also, the Actors runtime does not retain any information about the timers that were running before deactivation. 这主要归功于执行组件可以注册在将来重新激活时需要的任何计时器。It is up to the actor to register any timers that it needs when it is reactivated in the future. 有关详细信息,请参阅执行组件垃圾回收部分。For more information, see the section on actor garbage collection.

执行组件提醒Actor reminders

提醒是一种机制,用于在指定时间对执行组件触发持久回调。Reminders are a mechanism to trigger persistent callbacks on an actor at specified times. 其功能类似于计时器。Their functionality is similar to timers. 但与计时器不同的是,提醒会在所有情况下触发,直到执行组件显式注销提醒或显式删除执行组件。But unlike timers, reminders are triggered under all circumstances until the actor explicitly unregisters them or the actor is explicitly deleted. 具体而言,提醒会在执行组件停用和故障转移间触发,因为 执行组件运行时会使用执行组件状态提供程序保存有关执行组件提醒的信息。Specifically, reminders are triggered across actor deactivations and failovers because the Actors runtime persists information about the actor's reminders using actor state provider. 请注意,提醒的可靠性关系到执行组件状态提供程序提供的状态可靠性保证。Please note that the reliability of reminders is tied to the state reliability guarantees provided by the actor state provider. 这意味着,对于状态持久性设置为 None 的执行组件,故障转移后将不会触发提醒。This means that for actors whose state persistence is set to None, the reminders will not fire after a failover.

为了注册提醒,执行组件会调用基类上提供的 RegisterReminderAsync 方法,如以下示例中所示:To register a reminder, an actor calls the RegisterReminderAsync method provided on the base class, as shown in the following example:

protected override async Task OnActivateAsync()
{
    string reminderName = "Pay cell phone bill";
    int amountInDollars = 100;

    IActorReminder reminderRegistration = await this.RegisterReminderAsync(
        reminderName,
        BitConverter.GetBytes(amountInDollars),
        TimeSpan.FromDays(3),    //The amount of time to delay before firing the reminder
        TimeSpan.FromDays(1));    //The time interval between firing of reminders
}
@Override
protected CompletableFuture onActivateAsync()
{
    String reminderName = "Pay cell phone bill";
    int amountInDollars = 100;

    ActorReminder reminderRegistration = this.registerReminderAsync(
            reminderName,
            state,
            dueTime,    //The amount of time to delay before firing the reminder
            period);    //The time interval between firing of reminders
}

在本示例中, "Pay cell phone bill" 是提醒名称。In this example, "Pay cell phone bill" is the reminder name. 这是执行组件用来唯一标识提醒的字符串。This is a string that the actor uses to uniquely identify a reminder. BitConverter.GetBytes(amountInDollars)(C#) 是与提醒相关联的上下文。BitConverter.GetBytes(amountInDollars)(C#) is the context that is associated with the reminder. 它会作为提醒回调的参数传递回执行组件,即IRemindable.ReceiveReminderAsync(C#) 或 Remindable.receiveReminderAsync(Java)。It will be passed back to the actor as an argument to the reminder callback, i.e. IRemindable.ReceiveReminderAsync(C#) or Remindable.receiveReminderAsync(Java).

使用提醒的执行组件必须实现 IRemindable 接口,如以下示例中所示。Actors that use reminders must implement the IRemindable interface, as shown in the example below.

public class ToDoListActor : Actor, IToDoListActor, IRemindable
{
    public ToDoListActor(ActorService actorService, ActorId actorId)
        : base(actorService, actorId)
    {
    }

    public Task ReceiveReminderAsync(string reminderName, byte[] context, TimeSpan dueTime, TimeSpan period)
    {
        if (reminderName.Equals("Pay cell phone bill"))
        {
            int amountToPay = BitConverter.ToInt32(context, 0);
            System.Console.WriteLine("Please pay your cell phone bill of ${0}!", amountToPay);
        }
        return Task.FromResult(true);
    }
}
public class ToDoListActorImpl extends FabricActor implements ToDoListActor, Remindable
{
    public ToDoListActor(FabricActorService actorService, ActorId actorId)
    {
        super(actorService, actorId);
    }

    public CompletableFuture receiveReminderAsync(String reminderName, byte[] context, Duration dueTime, Duration period)
    {
        if (reminderName.equals("Pay cell phone bill"))
        {
            int amountToPay = ByteBuffer.wrap(context).getInt();
            System.out.println("Please pay your cell phone bill of " + amountToPay);
        }
        return CompletableFuture.completedFuture(true);
    }

触发提醒时,Reliable Actors 运行时会对执行组件调用 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 方法。When a reminder is triggered, the Reliable Actors runtime will invoke the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) method on the Actor. 一个执行组件可以注册多个提醒,而 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 方法会在触发其中任一提醒时调用。An actor can register multiple reminders, and the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) method is invoked when any of those reminders is triggered. 执行组件可以使用传入给 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 方法的提醒名称来找出触发的提醒。The actor can use the reminder name that is passed in to the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) method to figure out which reminder was triggered.

Actors 运行时会在 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 调用完成时保存执行组件的状态。The Actors runtime saves the actor's state when the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) call finishes. 如果保存状态时发生错误,则会停用该执行组件对象并激活一个新实例。If an error occurs in saving the state, that actor object will be deactivated and a new instance will be activated.

为了注销提醒,执行组件会调用 UnregisterReminderAsync(C#) 或 unregisterReminderAsync(Java) 方法,如以下示例所示。To unregister a reminder, an actor calls the UnregisterReminderAsync(C#) or unregisterReminderAsync(Java) method, as shown in the examples below.

IActorReminder reminder = GetReminder("Pay cell phone bill");
Task reminderUnregistration = UnregisterReminderAsync(reminder);
ActorReminder reminder = getReminder("Pay cell phone bill");
CompletableFuture reminderUnregistration = unregisterReminderAsync(reminder);

如上所示, UnregisterReminderAsync(C#) 或 unregisterReminderAsync(Java) 方法接受 IActorReminder(C#) 或 ActorReminder(Java) 接口。As shown above, the UnregisterReminderAsync(C#) or unregisterReminderAsync(Java) method accepts an IActorReminder(C#) or ActorReminder(Java) interface. 执行组件基类支持 GetReminder(C#) 或 getReminder(Java) 方法,该方法可用于通过传入提醒名称来检索 IActorReminder(C#) 或 ActorReminder(Java) 接口。The actor base class supports a GetReminder(C#) or getReminder(Java) method that can be used to retrieve the IActorReminder(C#) or ActorReminder(Java) interface by passing in the reminder name. 这十分方便,因为执行组件无需保存从 RegisterReminder(C#) 或 registerReminder(Java) 方法调用返回的 IActorReminder(C#) 或 ActorReminder(Java) 接口。This is convenient because the actor does not need to persist the IActorReminder(C#) or ActorReminder(Java) interface that was returned from the RegisterReminder(C#) or registerReminder(Java) method call.

后续步骤Next Steps

了解 Reliable Actor 事件和可重入性:Learn about Reliable Actor events and reentrancy: