Partilhar via


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 RegisterTimermé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 MoveObjectmé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 ReceiveReminderAsyncmétodo (C#) ou receiveReminderAsync(Java) no Ator. Um ator pode registrar vários lembretes, e o ReceiveReminderAsyncmétodo (C#) ou receiveReminderAsync(Java) é invocado quando qualquer um desses lembretes é acionado. O ator pode usar o nome do lembrete que é passado para o ReceiveReminderAsyncmé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 ReceiveReminderAsyncchamada (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 UnregisterReminderAsyncmé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 UnregisterReminderAsyncmétodo (C#) ou unregisterReminderAsync(Java) aceita uma IActorReminderinterface (C#) ou ActorReminder(Java). A classe base ator suporta um GetRemindermétodo (C#) ou getReminder(Java) que pode ser usado para recuperar a IActorReminderinterface (C#) ou ActorReminder(Java) passando o nome do lembrete. Isso é conveniente porque o ator não precisa persistir a IActorReminderinterface (C#) ou ActorReminder(Java) que foi retornada da chamada do RegisterRemindermétodo (C#) ou registerReminder(Java).

Passos Seguintes

Saiba mais sobre os eventos e reentrância do Reliable Ator: