Udostępnij za pośrednictwem


Czasomierze i przypomnienia

Środowisko Orleans uruchomieniowe udostępnia dwa mechanizmy nazywane czasomierzami i przypomnieniami, które umożliwiają deweloperowi określenie okresowego zachowania dla ziarna.

Czasomierze

Czasomierze służą do tworzenia okresowego zachowania ziarna, które nie jest wymagane do stosowania wielu aktywacji (wystąpień ziarna). Czasomierz jest identyczny z standardową klasą .NET System.Threading.Timer . Ponadto czasomierze podlegają gwarancjom wykonywania jednowątkowego w ramach aktywacji ziarna, na której działają, a ich wykonania są przeplatane z innymi żądaniami, tak jakby wywołanie zwrotne czasomierza było metodą ziarna oznaczoną jako AlwaysInterleaveAttribute.

Każda aktywacja może zawierać zero lub więcej skojarzonych z nim czasomierzy. Środowisko uruchomieniowe wykonuje procedurę czasomierza w kontekście środowiska uruchomieniowego, z którą jest skojarzona.

Użycie czasomierza

Aby uruchomić czasomierz, użyj RegisterGrainTimer metody , która zwraca IDisposable odwołanie:

protected IDisposable RegisterGrainTimer(
    Func<object, Task> callback,        // function invoked when the timer ticks
    object state,                       // object to pass to callback
    GrainTimerCreationOptions options)  // timer creation options

Aby anulować czasomierz, należy go usunąć.

Czasomierz przestaje wyzwalać, jeśli ziarno jest dezaktywowane lub gdy wystąpi błąd, a jego silos ulega awarii.

Ważne zagadnienia:

  • Po włączeniu zbierania aktywacji wykonanie wywołania zwrotnego czasomierza nie zmienia stanu aktywacji z bezczynności do użycia. Oznacza to, że czasomierz nie może służyć do odroczenia dezaktywacji w inny sposób bezczynnych aktywacji.
  • Okres przekazywany do Grain.RegisterGrainTimer to czas, który przechodzi od momentu, w którym zwracany przez callback program jest rozpoznawany do momentu, w którym Task powinno nastąpić następne wywołaniecallback. Nie tylko uniemożliwia to wykonywanie kolejnych wywołań do callback nakładania się, ale także sprawia, że callback czas potrzebny na ukończenie wpływa na częstotliwość wywoływania callback . Jest to ważne odchylenie od semantyki .System.Threading.Timer
  • Każde wywołanie callback elementu jest dostarczane do aktywacji na osobnym kolei i nigdy nie jest uruchamiane współbieżnie z innymi włącza tę samą aktywację. callback Wywołania nie są jednak dostarczane jako komunikaty i w związku z tym nie podlegają przeplataniu semantyki komunikatów. Oznacza to, że wywołania callback zachowują się tak, jakby ziarno jest ponownie biorące udział i wykonuje jednocześnie z innymi żądaniami ziarna. Aby użyć semantyki planowania żądań ziarna, można wywołać metodę ziarna w celu wykonania pracy wykonanej w ramach callback. Inną alternatywą jest użycie elementu AsyncLock lub .SemaphoreSlim Bardziej szczegółowe wyjaśnienie jest dostępne w problemie Orleans usługi GitHub #2574.

Przypomnienia

Przypomnienia są podobne do czasomierzy, z kilkoma ważnymi różnicami:

  • Przypomnienia są trwałe i nadal wyzwalane w niemal wszystkich sytuacjach (w tym częściowych lub pełnych ponownych uruchomień klastra), chyba że jawnie anulowano.
  • Przypomnienie "definicje" są zapisywane w magazynie. Jednak każde konkretne wystąpienie, z określonym czasem, nie jest. Ma to efekt uboczny, że jeśli klaster jest wyłączony w momencie określonego znacznika przypomnienia, zostanie pominięty i nastąpi tylko następny znacznik przypomnienia.
  • Przypomnienia są skojarzone z ziarnem, a nie z żadną konkretną aktywacją.
  • Jeśli ziarno nie ma aktywacji skojarzonej z nim po zaznaczeniu przypomnienia, ziarno jest tworzone. Jeśli aktywacja stanie się bezczynna i zostanie zdezaktywowana, przypomnienie skojarzone z tym samym ziarnem reaktywuje ziarno po następnym zaznaczeniu.
  • Dostarczanie przypomnień odbywa się za pośrednictwem wiadomości i podlega tej samej semantyce przeplatania co wszystkie inne metody ziarna.
  • Przypomnienia nie powinny być używane w przypadku czasomierzy o wysokiej częstotliwości — ich okres powinien być mierzony w minutach, godzinach lub dniach.

Konfigurowanie

Przypomnienia, stałe, polegają na magazynie do działania. Należy określić, które kopie zapasowe magazynu mają być używane przed funkcjami podsystemu przypomnienia. Jest to realizowane przez skonfigurowanie jednego z dostawców przypomnień za pomocą Use{X}ReminderService metod rozszerzenia, gdzie X jest nazwą dostawcy, na przykład UseAzureTableReminderService.

Konfiguracja tabeli platformy Azure:

// TODO replace with your connection string
const string connectionString = "YOUR_CONNECTION_STRING_HERE";
var silo = new HostBuilder()
    .UseOrleans(builder =>
    {
        builder.UseAzureTableReminderService(connectionString)
    })
    .Build();

SQL:

const string connectionString = "YOUR_CONNECTION_STRING_HERE";
const string invariant = "YOUR_INVARIANT";
var silo = new HostBuilder()
    .UseOrleans(builder =>
    {
        builder.UseAdoNetReminderService(options =>
        {
            options.ConnectionString = connectionString; // Redacted
            options.Invariant = invariant;
        });
    })
    .Build();

Jeśli chcesz po prostu zaimplementować symbol zastępczy przypomnień bez konieczności konfigurowania konta platformy Azure lub bazy danych SQL Database, zapewnia to implementację systemu przypomnień tylko do programowania:

var silo = new HostBuilder()
    .UseOrleans(builder =>
    {
        builder.UseInMemoryReminderService();
    })
    .Build();

Ważne

Jeśli masz heterogeniczny klaster, w którym silosy obsługują różne typy ziarna (implementują różne interfejsy), każdy silos musi dodać konfigurację przypomnień, nawet jeśli sam silos nie obsługuje żadnych przypomnień.

Użycie przypomnienia

Ziarno korzystające z przypomnień musi zaimplementować metodę IRemindable.ReceiveReminder .

Task IRemindable.ReceiveReminder(string reminderName, TickStatus status)
{
    Console.WriteLine("Thanks for reminding me-- I almost forgot!");
    return Task.CompletedTask;
}

Aby rozpocząć przypomnienie, użyj Grain.RegisterOrUpdateReminder metody , która zwraca IGrainReminder obiekt:

protected Task<IGrainReminder> RegisterOrUpdateReminder(
    string reminderName,
    TimeSpan dueTime,
    TimeSpan period)
  • reminderName: to ciąg, który musi jednoznacznie identyfikować przypomnienie w zakresie ziarna kontekstowego.
  • dueTime: określa ilość czasu oczekiwania przed wystawieniem znacznika czasomierza po raz pierwszy.
  • period: określa okres czasomierza.

Ponieważ przypomnienia przetrwają okres istnienia każdej pojedynczej aktywacji, muszą zostać jawnie anulowane (w przeciwieństwie do usuwania). Anuluj przypomnienie, wywołując polecenie Grain.UnregisterReminder:

protected Task UnregisterReminder(IGrainReminder reminder)

To reminder obiekt dojścia zwrócony przez Grain.RegisterOrUpdateReminderelement .

Wystąpienia elementu IGrainReminder nie mają gwarancji, że są prawidłowe poza cyklem życia aktywacji. Jeśli chcesz zidentyfikować przypomnienie w sposób, który będzie się powtarzać, użyj ciągu zawierającego nazwę przypomnienia.

Jeśli masz tylko nazwę przypomnienia i potrzebujesz odpowiedniego IGrainReminderwystąpienia , wywołaj metodę Grain.GetReminder :

protected Task<IGrainReminder> GetReminder(string reminderName)

Decydowanie o tym, którego użyć

Zalecamy używanie czasomierzy w następujących okolicznościach:

  • Jeśli nie ma znaczenia (lub jest pożądane), czasomierz przestaje działać, gdy aktywacja jest dezaktywowana lub występują błędy.
  • Rozdzielczość czasomierza jest mała (na przykład rozsądnie wyrażalna w sekundach lub minutach).
  • Wywołanie zwrotne czasomierza można uruchomić z Grain.OnActivateAsync() lub po wywołaniu metody ziarna.

Zalecamy używanie przypomnień w następujących okolicznościach:

  • Gdy okresowe zachowanie musi przetrwać aktywację i wszelkie błędy.
  • Wykonywanie rzadko wykonywanych zadań (na przykład rozsądnie wyrażanych w minutach, godzinach lub dniach).

Łączenie czasomierzy i przypomnień

Możesz rozważyć użycie kombinacji przypomnień i czasomierzy w celu osiągnięcia celu. Jeśli na przykład potrzebujesz czasomierza z małą rozdzielczością, która musi przetrwać w ramach aktywacji, możesz użyć przypomnienia uruchamianego co pięć minut, którego celem jest wznawianie ziarna, które ponownie uruchamia lokalny czasomierz, który mógł zostać utracony z powodu dezaktywacji.

Rejestracje ziarna POCO

Aby zarejestrować czasomierz lub przypomnienie z ziarnem POCO, należy zaimplementować IGrainBase interfejs i wstrzyknąć ITimerRegistry lub IReminderRegistry do konstruktora ziarna.

using Orleans.Timers;

namespace Timers;

public sealed class PingGrain : IGrainBase, IPingGrain, IDisposable
{
    private const string ReminderName = "ExampleReminder";

    private readonly IReminderRegistry _reminderRegistry;

    private IGrainReminder? _reminder;

    public  IGrainContext GrainContext { get; }

    public PingGrain(
        ITimerRegistry timerRegistry,
        IReminderRegistry reminderRegistry,
        IGrainContext grainContext)
    {
        // Register timer
        timerRegistry.RegisterGrainTimer(
            grainContext,
            callback: static async (state, cancellationToken) =>
            {
                // Omitted for brevity...
                // Use state

                await Task.CompletedTask;
            },
            state: this,
            options: new GrainTimerCreationOptions
            {
                DueTime = TimeSpan.FromSeconds(3),
                Period = TimeSpan.FromSeconds(10)
            });

        _reminderRegistry = reminderRegistry;

        GrainContext = grainContext;
    }

    public async Task Ping()
    {
        _reminder = await _reminderRegistry.RegisterOrUpdateReminder(
            callingGrainId: GrainContext.GrainId,
            reminderName: ReminderName,
            dueTime: TimeSpan.Zero,
            period: TimeSpan.FromHours(1));
    }

    void IDisposable.Dispose()
    {
        if (_reminder is not null)
        {
            _reminderRegistry.UnregisterReminder(
                GrainContext.GrainId, _reminder);
        }
    }
}

Powyższy kod ma następujące działanie:

  • Definiuje ziarno POCO, które implementuje IGrainBase, IPingGraini IDisposable.
  • Rejestruje czasomierz wywoływany co 10 sekund i uruchamia się 3 sekundy po rejestracji.
  • Po Ping wywołaniu program rejestruje przypomnienie, które jest wywoływane co godzinę i rozpoczyna się natychmiast po rejestracji.
  • Metoda Dispose anuluje przypomnienie, jeśli jest zarejestrowane.