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 przezcallback
program jest rozpoznawany do momentu, w którymTask
powinno nastąpić następne wywołaniecallback
. Nie tylko uniemożliwia to wykonywanie kolejnych wywołań docallback
nakładania się, ale także sprawia, żecallback
czas potrzebny na ukończenie wpływa na częstotliwość wywoływaniacallback
. 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łaniacallback
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 ramachcallback
. Inną alternatywą jest użycie elementuAsyncLock
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 IGrainReminder
wystą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,
IPingGrain
i 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.