Temporizadores e lembretes de atores
Os atores podem agendar trabalhos periódicos em si mesmos, registrando temporizadores ou lembretes. Este artigo mostra como usar temporizadores e lembretes e explica as diferenças entre eles.
Temporizadores de ator
Os temporizadores Ator fornecem um wrapper simples em torno de um temporizador .NET ou Java para garantir que os métodos de retorno de chamada respeitem as garantias de simultaneidade baseadas em turnos que o tempo de execução do Actors fornece.
Os atores podem usar os RegisterTimer
métodos (C#) ou registerTimer
(Java) e UnregisterTimer
(C#) ou unregisterTimer
(Java) em sua classe base para registrar e cancelar o registro de seus temporizadores. O exemplo abaixo mostra o uso de APIs de temporizador. As APIs são muito semelhantes ao temporizador .NET ou ao temporizador Java. Neste exemplo, quando o temporizador é devido, o tempo de execução de Actors chamará o MoveObject
método (C#) ou moveObject
(Java). O método é garantido para respeitar a simultaneidade baseada em turnos. Isso significa que nenhum outro método de ator ou retornos de chamada de temporizador/lembrete estará em andamento até que esse retorno de chamada conclua a execução.
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;});
});
}
}
O próximo período do temporizador começa depois que o retorno de chamada conclui a execução. Isso implica que o temporizador é interrompido enquanto o retorno de chamada está em execução e é iniciado quando o retorno de chamada termina.
O tempo de execução de Atores salva as alterações feitas no State Manager do ator quando o retorno de chamada termina. Se ocorrer um erro ao salvar o estado, esse objeto ator será desativado e uma nova instância será ativada.
Ao contrário dos lembretes, os temporizadores não podem ser atualizados. Se RegisterTimer
for chamado novamente, um novo temporizador será registrado.
Todos os temporizadores são interrompidos quando o ator é desativado como parte da coleta de lixo. Nenhum retorno de chamada de temporizador é invocado depois disso. Além disso, o tempo de execução do Actors não retém nenhuma informação sobre os temporizadores que estavam sendo executados antes da desativação. Cabe ao ator registrar os temporizadores de que precisar quando for reativado no futuro. Para obter mais informações, consulte a seção sobre coleta de lixo do ator.
Lembretes de atores
Os lembretes são um mecanismo para disparar retornos de chamada persistentes em um ator em momentos específicos. A sua funcionalidade é semelhante aos temporizadores. Mas, ao contrário dos temporizadores, os lembretes são acionados em todas as circunstâncias até que o ator explicitamente cancele o registro ou o ator seja explicitamente excluído. Especificamente, os lembretes são acionados em desativações e failovers do ator porque o tempo de execução do Actors persiste informações sobre os lembretes do ator usando o provedor de estado do ator. Também ao contrário dos temporizadores, os lembretes existentes podem ser atualizados chamando o método de registro (RegisterReminderAsync
) novamente usando o mesmo reminderName.
Nota
A fiabilidade dos lembretes está ligada às garantias de fiabilidade do Estado fornecidas pelo prestador estatal interveniente. Isso significa que, para atores cuja persistência de estado está definida como Nenhum, os lembretes não serão acionados após um failover.
Para registrar um lembrete, um ator chama o RegisterReminderAsync
método fornecido na classe base, conforme mostrado no exemplo a seguir:
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
}
Neste exemplo, "Pay cell phone bill"
é o nome do lembrete. Esta é uma cadeia de caracteres que o ator usa para identificar exclusivamente um lembrete. BitConverter.GetBytes(amountInDollars)
(C#) é o contexto associado ao lembrete. Ele será passado de volta para o ator como um argumento para o retorno de chamada de lembrete, ou seja IRemindable.ReceiveReminderAsync
, (C#) ou Remindable.receiveReminderAsync
(Java).
Os atores que usam lembretes devem implementar a IRemindable
interface, como mostrado no exemplo abaixo.
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);
}
Quando um lembrete é acionado, o tempo de execução dos Atores Confiáveis invoca o ReceiveReminderAsync
método (C#) ou receiveReminderAsync
(Java) no Ator. Um ator pode registrar vários lembretes, e o ReceiveReminderAsync
método (C#) ou receiveReminderAsync
(Java) é invocado quando qualquer um desses lembretes é acionado. O ator pode usar o nome do lembrete que é passado para o ReceiveReminderAsync
método (C#) ou receiveReminderAsync
(Java) para descobrir qual lembrete foi acionado.
O tempo de execução de Actors salva o estado do ator quando a ReceiveReminderAsync
chamada (C#) ou receiveReminderAsync
(Java) termina. Se ocorrer um erro ao salvar o estado, esse objeto ator será desativado e uma nova instância será ativada.
Para cancelar o registro de um lembrete, um ator chama o UnregisterReminderAsync
método (C#) ou unregisterReminderAsync
(Java), conforme mostrado nos exemplos abaixo.
IActorReminder reminder = GetReminder("Pay cell phone bill");
Task reminderUnregistration = await UnregisterReminderAsync(reminder);
ActorReminder reminder = getReminder("Pay cell phone bill");
CompletableFuture reminderUnregistration = unregisterReminderAsync(reminder);
Como mostrado acima, o UnregisterReminderAsync
método (C#) ou unregisterReminderAsync
(Java) aceita uma IActorReminder
interface (C#) ou ActorReminder
(Java). A classe base ator suporta um GetReminder
método (C#) ou getReminder
(Java) que pode ser usado para recuperar a IActorReminder
interface (C#) ou ActorReminder
(Java) passando o nome do lembrete. Isso é conveniente porque o ator não precisa persistir a IActorReminder
interface (C#) ou ActorReminder
(Java) que foi retornada da chamada do RegisterReminder
método (C#) ou registerReminder
(Java).
Passos Seguintes
Saiba mais sobre os eventos e reentrância do Reliable Ator: