Czasomierze i przypomnienia aktora

Aktorzy mogą planować okresową pracę nad sobą, rejestrując czasomierze lub przypomnienia. W tym artykule pokazano, jak używać czasomierzy i przypomnień oraz objaśniać różnice między nimi.

Czasomierze aktora

Czasomierze aktora zapewniają prostą otokę czasomierza platformy .NET lub Języka Java, aby upewnić się, że metody wywołania zwrotnego przestrzegają gwarancji współbieżności opartej na kolei zapewnianej przez środowisko uruchomieniowe Actors.

Aktorzy mogą używać RegisterTimermetod (C#) lub registerTimer(Java) i UnregisterTimer(C#) lub unregisterTimer(Java) w swojej klasie bazowej do rejestrowania i wyrejestrowania czasomierzy. W poniższym przykładzie pokazano użycie interfejsów API czasomierza. Interfejsy API są bardzo podobne do czasomierza platformy .NET lub czasomierza Języka Java. W tym przykładzie, gdy czasomierz jest należny, środowisko uruchomieniowe Actors wywoła MoveObjectmetodę (C#) lub moveObject(Java). Metoda ma gwarancję przestrzegania współbieżności opartej na kolei. Oznacza to, że żadne inne metody aktora ani wywołania zwrotne czasomierza/przypomnienia nie będą w toku do momentu zakończenia wykonywania tego wywołania zwrotnego.

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;});
        });
    }
}

Następny okres czasomierza rozpoczyna się po zakończeniu wykonywania wywołania zwrotnego. Oznacza to, że czasomierz jest zatrzymywany podczas wykonywania wywołania zwrotnego i jest uruchamiany po zakończeniu wywołania zwrotnego.

Środowisko uruchomieniowe Aktorów zapisuje zmiany wprowadzone w menedżerze stanu aktora po zakończeniu wywołania zwrotnego. Jeśli wystąpi błąd podczas zapisywania stanu, obiekt aktora zostanie zdezaktywowany i zostanie aktywowane nowe wystąpienie.

W przeciwieństwie do przypomnień czasomierze nie mogą być aktualizowane. Jeśli RegisterTimer zostanie ponownie wywołana, zostanie zarejestrowany nowy czasomierz.

Wszystkie czasomierze są zatrzymywane, gdy aktor jest dezaktywowany w ramach odzyskiwania pamięci. Po tym czasie nie są wywoływane wywołania zwrotne czasomierza. Ponadto środowisko uruchomieniowe aktorów nie zachowuje żadnych informacji o czasomierzach uruchomionych przed dezaktywacją. Należy do aktora, aby zarejestrować wszelkie czasomierze, których potrzebuje, gdy zostanie ponownie aktywowany w przyszłości. Aby uzyskać więcej informacji, zobacz sekcję dotyczącą odzyskiwania pamięci aktora.

Przypomnienia aktora

Przypomnienia są mechanizmem wyzwalania trwałych wywołań zwrotnych dla aktora w określonych godzinach. Ich funkcjonalność jest podobna do czasomierzy. Ale w przeciwieństwie do czasomierzy przypomnienia są wyzwalane we wszystkich okolicznościach, dopóki aktor jawnie ich nie wyrejestruje lub aktor zostanie jawnie usunięty. W szczególności przypomnienia są wyzwalane między dezaktywacjami aktorów i trybami failover, ponieważ środowisko uruchomieniowe Actors utrwala informacje o przypomnieniach aktora przy użyciu dostawcy stanu aktora. Ponadto w przeciwieństwie do czasomierzy istniejące przypomnienia można zaktualizować, wywołując ponownie metodę rejestracji (RegisterReminderAsync) przy użyciu tego samego przypomnieniaName.

Uwaga

Niezawodność przypomnień jest powiązana z gwarancjami niezawodności państwa dostarczonymi przez dostawcę stanu aktora. Oznacza to, że w przypadku aktorów, których trwałość stanu jest ustawiona na Wartość Brak, przypomnienia nie będą wyzwalane po przejściu w tryb failover.

Aby zarejestrować przypomnienie, aktor wywołuje RegisterReminderAsync metodę podaną w klasie bazowej, jak pokazano w poniższym przykładzie:

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
}

W tym przykładzie "Pay cell phone bill" jest to nazwa przypomnienia. Jest to ciąg używany przez aktora do unikatowego identyfikowania przypomnienia. BitConverter.GetBytes(amountInDollars)(C#) to kontekst skojarzony z przypomnieniem. Zostanie on przekazany z powrotem do aktora jako argument wywołania zwrotnego przypomnienia, tj. IRemindable.ReceiveReminderAsync(C#) lub Remindable.receiveReminderAsync(Java).

Aktorzy korzystający z przypomnień muszą zaimplementować IRemindable interfejs, jak pokazano w poniższym przykładzie.

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);
    }

Po wyzwoleniu przypomnienia środowisko uruchomieniowe Reliable Actors wywoła ReceiveReminderAsyncmetodę (C#) lub receiveReminderAsync(Java) w aktorze. Aktor może zarejestrować wiele przypomnień, a ReceiveReminderAsyncmetoda (C#) lub receiveReminderAsync(Java) jest wywoływana po wyzwoleniu dowolnego z tych przypomnień. Aktor może użyć nazwy przypomnienia przekazanej ReceiveReminderAsyncdo metody (C#) lub receiveReminderAsync(Java), aby ustalić, które przypomnienie zostało wyzwolone.

Środowisko uruchomieniowe Actors zapisuje stan aktora po zakończeniu ReceiveReminderAsyncwywołania (C#) lub receiveReminderAsync(Java). Jeśli wystąpi błąd podczas zapisywania stanu, obiekt aktora zostanie zdezaktywowany i zostanie aktywowane nowe wystąpienie.

Aby wyrejestrować przypomnienie, aktor wywołuje UnregisterReminderAsyncmetodę (C#) lub unregisterReminderAsync(Java), jak pokazano w poniższych przykładach.

IActorReminder reminder = GetReminder("Pay cell phone bill");
Task reminderUnregistration = await UnregisterReminderAsync(reminder);
ActorReminder reminder = getReminder("Pay cell phone bill");
CompletableFuture reminderUnregistration = unregisterReminderAsync(reminder);

Jak pokazano powyżej, UnregisterReminderAsyncmetoda (C#) lub unregisterReminderAsync(Java) akceptuje IActorReminderinterfejs (C#) lub ActorReminder(Java). Klasa bazowa aktora obsługuje metodę GetReminder(C#) lub getReminder(Java), która może służyć do pobierania interfejsu IActorReminder(C#) lub ActorReminder(Java), przekazując nazwę przypomnienia. Jest to wygodne, ponieważ aktor nie musi utrwalać interfejsu IActorReminder(C#) lub ActorReminder(Java), który został zwrócony z RegisterReminderwywołania metody (C#) lub registerReminder(Java).

Następne kroki

Dowiedz się więcej o zdarzeniach reliable actor i ponownej instalacji: