Časovače a připomenutí
Modul Orleans runtime poskytuje dva mechanismy označované jako časovače a připomenutí, které vývojářům umožňují určit pravidelné chování zrn.
Časovače
Časovače se používají k vytváření pravidelného chování agregačního intervalu, které není potřeba k rozprostření více aktivací (vytvoření instance zrnka). Časovač je shodný se standardní třídou .NET System.Threading.Timer . Kromě toho časovače podléhají zárukám jednovláknového provedení v rámci aktivace jádra, na které pracují.
Každá aktivace může mít přiřazených nula nebo více časovačů. Modul runtime spustí každou rutinu časovače v kontextu modulu runtime aktivace, ke které je přidružená.
Využití časovače
Pokud chcete spustit časovač, použijte metodu RegisterGrainTimer
IGrainTimer , která vrací odkaz:
protected IGrainTimer RegisterGrainTimer<TState>(
Func<TState, CancellationToken, Task> callback, // function invoked when the timer ticks
TState state, // object to pass to callback
GrainTimerCreationOptions options) // timer creation options
Pokud chcete časovač zrušit, odstraňte ho.
Časovač přestane spouštět, pokud se agregační interval deaktivuje nebo když dojde k chybě a dojde k chybovému ukončení jeho sila.
Důležité aspekty:
- Pokud je povolená aktivační kolekce, spuštění zpětného volání časovače nezmění stav aktivace z nečinnosti na použití. To znamená, že časovač nelze použít k odložení deaktivace jinak nečinných aktivací.
- Uplynulé období
Grain.RegisterGrainTimer
je doba, která uplynula od okamžikuTask
, kdy je vrácenacallback
, vyřešena až do okamžiku, kdy by mělo dojít k dalšímucallback
vyvolání. To nejen znemožňuje následným volánímcallback
překrývat se, ale také umožňuje, aby dobacallback
trvání dokončení ovlivnila frekvenci, při kterécallback
je vyvolána. To je důležitá odchylka od sémantiky System.Threading.Timer. - Každé vyvolání
callback
se doručí do aktivace na samostatném turnu a nikdy neběží souběžně s jinými zapnutími stejné aktivace. - Zpětná volání se ve výchozím nastavení neprolínají. Prokládání je možné povolit nastavením možnosti Interleave na hodnotu true u GrainTimerCreationOptions.
- Časovače lze aktualizovat pomocí metody Change(TimeSpan, TimeSpan) u vrácené instance IGrainTimer.
- Zpětná volání mohou udržovat zrno aktivní, čímž se zabrání jeho uvolnění, pokud je období časovače relativně krátké. To je možné povolit nastavením KeepAlive na true u GrainTimerCreationOptions.
- Zpětná volání mohou obdržet token zrušení, který se zruší, když je časovač likvidován nebo když se objekt začne deaktivovat.
- Zpětná volání mohou uvolnit časovač zrna, který je spustil.
- Zpětná volání podléhají filtrům jemnosti volání.
- Zpětná volání jsou viditelná v distribuovaném trasování, pokud je povolené distribuované trasování.
- Třídy POCO (třídy, které nedědí z Grain) mohou registrovat časovače zrn pomocí metody rozšíření RegisterGrainTimer.
Připomenutí
Připomenutí se podobají časovačům s několika důležitými rozdíly:
- Připomenutí jsou trvalá a stále se aktivují téměř ve všech situacích (včetně částečného nebo úplného restartování clusteru), pokud se explicitně nezruší.
- Připomenutí "definice" se zapisují do úložiště. Každý konkrétní výskyt s konkrétním časem ale není. To má vedlejší účinek, že pokud je cluster v době konkrétního zaškrtnutí připomenutí dole, zmešká se a dojde pouze k dalšímu zaškrtnutí připomenutí.
- Připomenutí jsou přidružená k odstupňovanému intervalu, ne ke konkrétní aktivaci.
- Pokud se k němu při odškrtnutí připomenutí nepřidružuje žádná aktivace, vytvoří se agregační interval. Pokud se aktivace stane nečinnou a deaktivuje se, připomenutí přidružené ke stejnému agregačnímu intervalu se znovu aktivuje, jakmile příště zaškrtne.
- Doručení připomenutí probíhá prostřednictvím zprávy a podléhá stejné sémantice prokládání jako všechny ostatní metody agregace.
- Připomenutí by se neměla používat pro časovače s vysokou frekvencí – jejich období by se mělo měřit v minutách, hodinách nebo dnech.
Konfigurace
Připomenutí, trvalá, spoléhají na fungování úložiště. Před funkcemi subsystému připomenutí musíte určit, které úložiště se má použít. To se provádí konfigurací jednoho z poskytovatelů připomenutí prostřednictvím Use{X}ReminderService
rozšiřujících metod, kde X
je název poskytovatele, UseAzureTableReminderServicenapříklad .
Konfigurace tabulky 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();
Pokud chcete, aby jenom zástupná implementace připomenutí fungovala, aniž byste museli nastavovat účet Azure nebo databázi SQL, získáte implementaci systému připomenutí, která je jen pro vývoj:
var silo = new HostBuilder()
.UseOrleans(builder =>
{
builder.UseInMemoryReminderService();
})
.Build();
Důležité
Pokud máte heterogenní cluster, kde sila zpracovávají různé typy zrnek (implementují různá rozhraní), musí každý sil přidat konfiguraci pro připomenutí, i když samotný silo nezpracuje žádná připomenutí.
Použití připomenutí
Odstupňované zpracování, které používá připomenutí, musí implementovat metodu IRemindable.ReceiveReminder .
Task IRemindable.ReceiveReminder(string reminderName, TickStatus status)
{
Console.WriteLine("Thanks for reminding me-- I almost forgot!");
return Task.CompletedTask;
}
Pokud chcete zahájit připomenutí, použijte metodu Grain.RegisterOrUpdateReminderIGrainReminder , která vrací objekt:
protected Task<IGrainReminder> RegisterOrUpdateReminder(
string reminderName,
TimeSpan dueTime,
TimeSpan period)
-
reminderName
: je řetězec, který musí jedinečně identifikovat připomenutí v rámci rozsahu kontextového agregačního intervalu. -
dueTime
: určuje množství času čekání před vydáním prvního časovače. -
period
: určuje období časovače.
Vzhledem k tomu, že připomenutí přežijí životnost jakékoli jediné aktivace, musí být explicitně zrušena (na rozdíl od vyřazení). Připomenutí zrušíte voláním Grain.UnregisterReminder:
protected Task UnregisterReminder(IGrainReminder reminder)
Je objekt reminder
popisovače vrácený objektem Grain.RegisterOrUpdateReminder.
Instance nejsou zaručené IGrainReminder
, že budou platné i po dobu životnosti aktivace. Pokud chcete identifikovat připomenutí způsobem, který přetrvává, použijte řetězec obsahující název připomenutí.
Pokud máte pouze název připomenutí a potřebujete odpovídající instanci IGrainReminder
, zavolejte metodu Grain.GetReminder :
protected Task<IGrainReminder> GetReminder(string reminderName)
Rozhodněte se, které použít
Doporučujeme používat časovače za následujících okolností:
- Pokud nezáleží (nebo je žádoucí), že časovač přestane fungovat, když se aktivace deaktivuje nebo dojde k selhání.
- Rozlišení časovače je malé (například přiměřeně vyjádřitelné v sekundách nebo minutách).
- Zpětné volání časovače může být spuštěno nebo Grain.OnActivateAsync() při vyvolání metody agregačního intervalu.
Doporučujeme používat připomenutí za následujících okolností:
- Když pravidelné chování potřebuje přežít aktivaci a případná selhání.
- Provádění zřídka používaných úkolů (například přiměřeně vyjádřitelné v minutách, hodinách nebo dnech).
Kombinování časovačů a připomenutí
K dosažení cíle můžete zvážit použití kombinace připomenutí a časovačů. Pokud například potřebujete časovač s malým rozlišením, které musí přežít napříč aktivacemi, můžete použít připomenutí, které se spustí každých pět minut, jehož účelem je probudit zrno, které restartuje místní časovač, který mohl být ztracen z důvodu deaktivace.
Registrace agregačního intervalu POCO
Pokud chcete zaregistrovat časovač nebo připomenutí v agregačním intervalu POCO, implementujete IGrainBase rozhraní a vložíte ho ITimerRegistry nebo IReminderRegistry do konstruktoru zrnka.
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);
}
}
}
Předchozí kód:
- Definuje agregační interval POCO, který implementuje IGrainBase,
IPingGrain
a IDisposable. - Zaregistruje časovač, který se vyvolá každých 10 sekund, a spustí 3 sekundy po registraci.
- Při
Ping
zavolání zaregistruje připomenutí, které se vyvolá každou hodinu, a začne okamžitě po registraci. - Metoda
Dispose
zruší připomenutí, pokud je zaregistrovaná.