角色可以通过注册计时器或提醒来安排周期性任务。 本文介绍如何使用计时器和提醒,并说明它们之间的差异。
演员计时器
执行组件计时器围绕 .NET 或 Java 计时器提供简单的包装器,以确保回调方法遵循 Actors 运行时提供的基于轮次的并发保证。
执行组件可以使用其基类上的 RegisterTimer(C#) 或 registerTimer(Java) 和 UnregisterTimer(C#) 或 unregisterTimer(Java) 方法注册和注销其计时器。 下面的示例显示了计时器 API 的使用。 API 与 .NET 计时器或 Java 计时器非常相似。 在此示例中,当计时器到期时,Actors 运行时将调用 MoveObject(C#) 或 moveObject(Java) 方法。 此方法保证遵循基于轮次的并发。 这意味着,在完成此回调之前,不会进行其他执行组件方法或计时器/提醒回调。
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;});
});
}
}
回调完成执行后,计时器的下一个周期将启动。 这意味着在回调执行时停止计时器,并在回调完成时启动。
执行组件运行时会在回调完成后保存对执行组件的状态管理器所做的更改。 如果在保存状态时发生错误,该执行组件对象将被停用,并且将激活一个新实例。
与 提醒不同,计时器无法更新。 如果 RegisterTimer 再次调用,则会注册新的计时器。
当参与者作为垃圾回收的一部分被停用时,所有计时器都会停止。 之后,系统不会调用计时器回调。 此外,Actors 运行时不会保留有关停用之前运行的计时器的任何信息。 由参与者在将来重新激活时自行注册其所需的任何计时器。 有关详细信息,请参阅 演员垃圾回收的章节。
执行组件提醒
提醒是一种机制,用于在指定时间对执行组件触发持久回调。 其功能类似于计时器。 但与计时器不同,提醒在所有情况下都会被触发,直到参与者显式注销它们或显式删除参与者。 具体而言,参与者停用和故障转移过程中会触发提醒,因为 Actor 运行时将通过参与者状态提供程序持久化保留有关参与者提醒的信息。 与计时器不同,可以通过使用相同的 RegisterReminderAsync 再次调用注册方法()来更新现有提醒。
注释
提醒的可靠性与 actor 状态提供者所提供的状态可靠性保证相关联。 这意味着,对于状态持久性设置为 “无”的执行组件,故障转移后不会触发提醒。
若要注册提醒,执行组件调用 RegisterReminderAsync 基类上提供的方法,如以下示例所示:
protected override async Task OnActivateAsync()
{
string reminderName = "Pay cell phone bill";
int amountInmoney = 100;
IActorReminder reminderRegistration = await this.RegisterReminderAsync(
reminderName,
BitConverter.GetBytes(amountInmoney),
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 amountInmoney = 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" 是提醒名称。 这是执行组件用于唯一标识提醒的字符串。
BitConverter.GetBytes(amountInmoney)(C#) 是与提醒关联的上下文。 它将作为参数被传递回给提醒回调的执行者,即 IRemindable.ReceiveReminderAsync(C#)或 Remindable.receiveReminderAsync(Java)。
使用提醒的执行组件必须实现 IRemindable 接口,如以下示例所示。
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 运行时将在 Actor 上调用 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 方法。 一个角色可以注册多个提醒,并在任何提醒被触发时调用 ReceiveReminderAsync(C#) 或 receiveReminderAsync(Java) 方法。 参与者可以使用传递给ReceiveReminderAsync(C#)或receiveReminderAsync(Java)方法的提醒名称来确定触发了哪个提醒。
行为体运行时在(C#)或(Java)调用完成时保存行为体的状态。 如果在保存状态时发生错误,该执行组件对象将被停用,并且将激活一个新实例。
若要取消注册提醒,执行组件将调用 UnregisterReminderAsync(C#) 或 unregisterReminderAsync(Java) 方法,如以下示例所示。
IActorReminder reminder = GetReminder("Pay cell phone bill");
Task reminderUnregistration = await UnregisterReminderAsync(reminder);
ActorReminder reminder = getReminder("Pay cell phone bill");
CompletableFuture reminderUnregistration = unregisterReminderAsync(reminder);
如上所示, UnregisterReminderAsync(C#)或 unregisterReminderAsync(Java)方法接受 IActorReminder(C#) 或 ActorReminder(Java) 接口。 演员基类支持一个可以通过传入提醒名称来检索接口的GetReminder(C#)或getReminder(Java)方法。 这很方便,因为执行组件不需要保留 IActorReminder从 (C#) 或 ActorReminder(Java) 方法调用返回的 RegisterReminder(C#) 或 registerReminder(Java) 接口。
后续步骤
了解 Reliable Actor 事件和重入问题: