Timer e promemoria degli attoriActor timers and reminders

Gli attori possono pianificare il relativo lavoro periodico registrando timer o promemoria.Actors can schedule periodic work on themselves by registering either timers or reminders. Questo articolo illustra come usare timer e promemoria e ne spiega le differenze.This article shows how to use timers and reminders and explains the differences between them.

Timer degli attoriActor timers

I timer degli attori forniscono un wrapper semplice intorno al timer .NET o Java per garantire che i metodi di callback rispettino le garanzie di concorrenza basata su turni offerte dal runtime di 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.

Per eseguire e annullare la registrazione dei timer, gli attori possono usare i metodi RegisterTimer(C#) o registerTimer(Java) e UnregisterTimer(C#) o unregisterTimer(Java) nella propria classe base.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. L'esempio seguente illustra l'uso delle API di timer,The example below shows the use of timer APIs. che sono molto simili al timer .NET o al timer Java.The APIs are very similar to the .NET timer or Java timer. In questo esempio, quando il timer è in scadenza, il runtime di Actors chiama il metodo MoveObject(C#) o moveObject(Java).In this example, when the timer is due, the Actors runtime will call the MoveObject(C#) or moveObject(Java) method. Si garantisce che il metodo rispetti la concorrenza basata su turni.The method is guaranteed to respect the turn-based concurrency. Ciò significa che nessun altro metodo di attori o callback di timer/promemoria sarà in azione fino al completamento dell'esecuzione del callback.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;});
        });
    }
}

Il periodo successivo del timer inizia dopo il completamento del callback.The next period of the timer starts after the callback completes execution. Pertanto, il timer viene arrestato mentre il callback è in esecuzione e viene avviato quando il callback è completato.This implies that the timer is stopped while the callback is executing and is started when the callback finishes.

Il runtime di Actors salva le modifiche apportate alla gestione stati dell'attore al termine del callback.The Actors runtime saves changes made to the actor's State Manager when the callback finishes. Se si verifica un errore durante il salvataggio dello stato, viene disattivato l'oggetto attore e viene attivata una nuova istanza.If an error occurs in saving the state, that actor object will be deactivated and a new instance will be activated.

Tutti i timer vengono arrestati quando l'attore viene disattivato come parte di garbage collection.All timers are stopped when the actor is deactivated as part of garbage collection. In seguito, non viene richiamato nessun callback di timer.No timer callbacks are invoked after that. Inoltre, il runtime di Actors non mantiene alcuna informazione sui timer in esecuzione prima della disattivazione.Also, the Actors runtime does not retain any information about the timers that were running before deactivation. È responsabilità dell'attore registrare gli eventuali timer che saranno necessari quando verrà riattivato in futuro.It is up to the actor to register any timers that it needs when it is reactivated in the future. Per ulteriori informazioni, vedere la sezione sulla garbage collection degli attori.For more information, see the section on actor garbage collection.

Promemoria degli attoriActor reminders

I promemoria sono un meccanismo per attivare i callback persistenti su un attore in base a orari specificati.Reminders are a mechanism to trigger persistent callbacks on an actor at specified times. La loro funzionalità è simile a quella dei timer.Their functionality is similar to timers. Tuttavia, a differenza dei timer, i promemoria vengono attivati in qualsiasi circostanza finché l'attore non ne annulla la registrazione in modo esplicito o finché l'attore non viene eliminato in modo esplicito.But unlike timers, reminders are triggered under all circumstances until the actor explicitly unregisters them or the actor is explicitly deleted. In particolare, i promemoria vengono attivati anche in caso di failover e disattivazione dell'attore perché il runtime di Actors rende persistenti le informazioni sui promemoria dell'attore tramite il provider di stato dell'attore.Specifically, reminders are triggered across actor deactivations and failovers because the Actors runtime persists information about the actor's reminders using actor state provider. Si noti che l'affidabilità dei promemoria è collegata alle garanzie di affidabilità degli stati fornite dal provider di stato dell'attore.Please note that the reliability of reminders is tied to the state reliability guarantees provided by the actor state provider. Questo significa che per gli attori in cui la persistenza dello stato è impostata su None, non verrà attivato alcun promemoria dopo un failover.This means that for actors whose state persistence is set to None, the reminders will not fire after a failover.

Per registrare un promemoria, un attore chiama il metodo RegisterReminderAsync fornito nella classe base, come illustrato nell'esempio seguente.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),
        TimeSpan.FromDays(1));
}
@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
}

In questo esempio "Pay cell phone bill" è il nome del promemoria.In this example, "Pay cell phone bill" is the reminder name. Questa è una stringa usata dall'attore per identificare in modo univoco un promemoria.This is a string that the actor uses to uniquely identify a reminder. BitConverter.GetBytes(amountInDollars)(C#) è il contesto associato al promemoria.BitConverter.GetBytes(amountInDollars)(C#) is the context that is associated with the reminder. Questo contesto verrà passato all'attore come argomento per il callback di promemoria, ad esempio IRemindable.ReceiveReminderAsync(C#) o 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).

Gli attori che usano i promemoria devono implementare l'interfaccia IRemindable , come illustrato nell'esempio seguente.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);
    }

Quando viene attivato un promemoria, il runtime di Reliable Actors richiama il metodo ReceiveReminderAsync(C#) o receiveReminderAsync(Java) sull'Actor.When a reminder is triggered, the Reliable Actors runtime will invoke the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) method on the Actor. Un attore può registrare più promemoria e il metodo ReceiveReminderAsync(C#) o receiveReminderAsync(Java) viene richiamato ogni volta che tali promemoria vengono attivati.An actor can register multiple reminders, and the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) method is invoked when any of those reminders is triggered. L'attore può usare il nome del promemoria che viene passato al metodo ReceiveReminderAsync(C#) o receiveReminderAsync(Java) per identificare il promemoria attivato.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.

Il runtime di Actors salva lo stato dell'attore al termine della chiamata a ReceiveReminderAsync(C#) o receiveReminderAsync(Java).The Actors runtime saves the actor's state when the ReceiveReminderAsync(C#) or receiveReminderAsync(Java) call finishes. Se si verifica un errore durante il salvataggio dello stato, viene disattivato l'oggetto attore e viene attivata una nuova istanza.If an error occurs in saving the state, that actor object will be deactivated and a new instance will be activated.

Per annullare la registrazione di un promemoria, un attore chiama il metodo UnregisterReminderAsync(C#) o unregisterReminderAsync(Java), come illustrato nell'esempio seguente.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);

Come mostrato in precedenza, il metodo UnregisterReminderAsync(C#) o unregisterReminderAsync(Java) accetta un'interfaccia IActorReminder(C#) o ActorReminder(Java).As shown above, the UnregisterReminderAsync(C#) or unregisterReminderAsync(Java) method accepts an IActorReminder(C#) or ActorReminder(Java) interface. La classe base dell'attore supporta un metodo GetReminder(C#) o getReminder(Java) che può essere usato per recuperare l'interfaccia IActorReminder(C#) o ActorReminder(Java) passando il nome del promemoria.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. Questo metodo è utile perché l'attore non deve rendere persistente l'interfaccia IActorReminder(C#) o ActorReminder(Java) restituita dalla chiamata al metodo RegisterReminder(C#) o registerReminder(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.

Passaggi successiviNext Steps

Acquisire informazioni sugli eventi e sulla rientranza di Reliable Actor:Learn about Reliable Actor events and reentrancy: