Таймеры актеров и напоминания

Актеры могут планировать периодические персональные задачи, регистрируя таймеры или напоминания. В этой статье показано, как использовать таймеры и напоминания и объяснить различия между ними.

Таймеры актеров

Таймеры актеров предоставляют простую оболочку для таймера .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;});
        });
    }
}

Следующий период таймера начинается после завершения выполнения обратного вызова. Это означает, что таймер останавливается во время выполнения обратного вызова и запускается после завершения обратного вызова.

Среда выполнения Actors сохраняет изменения, внесенные в диспетчер состояний актора после завершения обратного вызова. Если при сохранении состояния возникает ошибка, объект субъекта будет деактивирован, а новый экземпляр будет активирован.

В отличие от напоминаний, таймеры не могут быть обновлены. При RegisterTimer повторном вызове будет зарегистрирован новый таймер.

Все таймеры остановлены при деактивации субъекта в рамках сборки мусора. После этого обратные вызовы таймера не вызываются. Кроме того, среда выполнения Actors не сохраняет никаких сведений о таймерах, запущенных перед деактивацией. Актору следует зарегистрировать любые необходимые таймеры при повторной активации в будущем. Дополнительные сведения см. в разделе о сборке мусора акторов.

Напоминания актеров

Напоминания — это механизм для активации постоянных обратных вызовов для субъекта в определенное время. Их функциональность аналогична таймерам. Но в отличие от таймеров напоминания активируются во всех обстоятельствах, пока субъект явно не отменяет их регистрацию или субъект явно удаляется. В частности, напоминания срабатывают во время деактивации акторов и отказоустойчивости, так как среда выполнения акторов сохраняет информацию о напоминаниях актора с помощью поставщика состояния актора. Кроме того, в отличие от таймеров, существующие напоминания можно обновить, вызвав метод регистрации (RegisterReminderAsync) еще раз с помощью того же имени напоминания.

Замечание

Надежность напоминаний связана с гарантиями надежности, предоставляемыми провайдером состояния актора. Это означает, что для акторов, у которых сохраняемость состояния установлена в None, напоминания не будут срабатывать после сбоя.

Чтобы зарегистрировать напоминание, субъект вызывает RegisterReminderAsync метод, предоставленный в базовом классе, как показано в следующем примере:

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" — имя напоминания. Это строка, которую субъект использует для уникальной идентификации напоминания. BitConverter.GetBytes(amountInDollars)(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);
    }

При срабатывании напоминания среда выполнения Надежных актеров вызовет ReceiveReminderAsyncметод (C#) или receiveReminderAsync(Java) на актере. Субъект может зарегистрировать несколько напоминаний, а ReceiveReminderAsyncметод (C#) или receiveReminderAsync(Java) вызывается при активации любого из этих напоминаний. Субъект может использовать имя напоминания, переданное методу ReceiveReminderAsync(C#) или receiveReminderAsync(Java), чтобы выяснить, какое напоминание было активировано.

Среда выполнения Actors сохраняет состояние актора после завершения вызова ReceiveReminderAsync(C#) или receiveReminderAsync(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) интерфейс. Базовый класс актёра поддерживает метод для C# или Java, который можно использовать для получения интерфейса C# или Java, передав имя напоминания. Это удобно, поскольку актору не нужно сохранять интерфейс для IActorReminder(C#) или ActorReminder(Java), возвращаемый от вызова метода RegisterReminder(C#) или registerReminder(Java).

Дальнейшие шаги

Узнайте о событиях Надежных субъектов и повторном входе: