Timers en herinneringen
De Orleans runtime biedt twee mechanismen, timers en herinneringen genoemd, waarmee de ontwikkelaar periodiek gedrag voor korrels kan opgeven.
Timers
Timers worden gebruikt om periodiek korrelgedrag te maken dat niet vereist is om meerdere activeringen te omvatten (instantiëringen van het graan). Een timer is identiek aan de standaard .NET-klasse System.Threading.Timer . Bovendien zijn timers onderworpen aan garanties voor uitvoering met één thread binnen de korrelactivering waarop ze werken.
Aan elke activering zijn mogelijk nul of meer timers gekoppeld. De runtime voert elke timerroutine uit in de runtimecontext van de activering waaraan deze is gekoppeld.
Timergebruik
Als u een timer wilt starten, gebruikt u de RegisterGrainTimer
methode die een IGrainTimer verwijzing retourneert:
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
Als u de timer wilt annuleren, moet u deze verwijderen.
Een timer stopt met activeren als het graan wordt gedeactiveerd of wanneer er een fout optreedt en de silo vastloopt.
Belangrijke overwegingen:
- Wanneer activeringsverzameling is ingeschakeld, verandert de uitvoering van een timer-callback de status van de activering niet van inactief naar in gebruik. Dit betekent dat een timer niet kan worden gebruikt om de deactivering van anders inactieve activeringen uit te stellen.
- De doorgegeven
Grain.RegisterGrainTimer
periode is de hoeveelheid tijd die is verstreken vanaf het moment dat deTask
geretourneerde datumcallback
is opgelost tot het moment dat de volgende aanroep vancallback
moet plaatsvinden. Dit maakt het niet alleen onmogelijk voor opeenvolgende aanroepen om elkaar tecallback
overlappen, maar zorgt er ook voor dat de tijdsduurcallback
die nodig is om te voltooien van invloed is op de frequentie waarmeecallback
wordt aangeroepen. Dit is een belangrijke afwijking van de semantiek van System.Threading.Timer. - Elke aanroep van
callback
wordt geleverd aan een activering op een afzonderlijke beurt en wordt nooit gelijktijdig uitgevoerd met andere inschakelingen van dezelfde activering. - Callbacks interleave niet standaard. Interleaving kan worden ingeschakeld door Interleave in te stellen op true op GrainTimerCreationOptions.
- Graantimers kunnen worden bijgewerkt met behulp van methode Change(TimeSpan, TimeSpan) op het geretourneerde IGrainTimer-exemplaar.
- Callbacks kunnen het graan actief houden, waardoor het niet kan worden verzameld als de timerperiode relatief kort is. Dit kan worden ingeschakeld door KeepAlive in te stellen op true op GrainTimerCreationOptions.
- Callbacks kunnen een CancellationToken ontvangen dat wordt geannuleerd als de timer is verwijderd of wanneer de grain begint te deactiveren.
- Callbacks kunnen de graantimer verwijderen die ze heeft geactiveerd.
- Callbacks zijn onderhevig aan grain call-filters.
- Callbacks zijn zichtbaar in gedistribueerde tracering wanneer gedistribueerde tracering is ingeschakeld.
- POCO-korrels (graanklassen die niet overnemen van Grain) kunnen graantimers registreren met behulp van de extensiemethode RegisterGrainTimer.
Herinneringen
Herinneringen zijn vergelijkbaar met timers, met enkele belangrijke verschillen:
- Herinneringen zijn permanent en blijven activeren in bijna alle situaties (inclusief gedeeltelijke of volledige clusterherstarts) tenzij expliciet geannuleerd.
- Herinnering 'definities' worden naar de opslag geschreven. Elke specifieke gebeurtenis, met zijn specifieke tijd, is dat echter niet. Dit heeft het neveneffect dat als het cluster offline is op het moment van een specifieke herinneringstik, deze wordt gemist en alleen het volgende vinkje van de herinnering plaatsvindt.
- Herinneringen zijn gekoppeld aan een korrel, niet aan een specifieke activering.
- Als er geen activering aan een graan is gekoppeld wanneer er een herinnering wordt aangevinkt, wordt het graan gemaakt. Als een activering niet actief wordt en wordt gedeactiveerd, wordt het graan opnieuw geactiveerd wanneer deze vervolgens wordt aangevinkt.
- Herinneringsbezorging vindt plaats via een bericht en is onderhevig aan dezelfde interleaving semantiek als alle andere graanmethoden.
- Herinneringen mogen niet worden gebruikt voor timers met een hoge frequentie. Hun periode moet worden gemeten in minuten, uren of dagen.
Configuratie
Herinneringen, blijvend, zijn afhankelijk van opslag om te functioneren. U moet opgeven welke opslagback-up moet worden gebruikt voordat de subsysteemfuncties voor herinneringen worden gebruikt. Dit wordt gedaan door een van de herinneringsproviders te configureren via Use{X}ReminderService
extensiemethoden, waarbij X
bijvoorbeeld de naam van de provider UseAzureTableReminderServiceis.
Azure Table-configuratie:
// 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();
Als u alleen een tijdelijke aanduiding voor de implementatie van herinneringen wilt laten werken zonder dat u een Azure-account of SQL-database hoeft in te stellen, krijgt u dan een implementatie die alleen kan worden geïmplementeerd voor ontwikkeling van het herinneringssysteem:
var silo = new HostBuilder()
.UseOrleans(builder =>
{
builder.UseInMemoryReminderService();
})
.Build();
Belangrijk
Als u een heterogene cluster hebt, waarbij de silo's verschillende graantypen verwerken (verschillende interfaces implementeren), moet elke silo de configuratie voor Herinneringen toevoegen, zelfs als de silo zelf geen herinneringen afhandelt.
Gebruik van herinneringen
Een graan dat herinneringen gebruikt, moet de IRemindable.ReceiveReminder methode implementeren.
Task IRemindable.ReceiveReminder(string reminderName, TickStatus status)
{
Console.WriteLine("Thanks for reminding me-- I almost forgot!");
return Task.CompletedTask;
}
Als u een herinnering wilt starten, gebruikt u de Grain.RegisterOrUpdateReminder methode die een IGrainReminder object retourneert:
protected Task<IGrainReminder> RegisterOrUpdateReminder(
string reminderName,
TimeSpan dueTime,
TimeSpan period)
-
reminderName
: is een tekenreeks die de herinnering uniek moet identificeren binnen het bereik van het contextuele graan. -
dueTime
: geeft een hoeveelheid tijd op die moet worden gewacht voordat de eerste timer tick wordt uitgegeven. -
period
: geeft de periode van de timer op.
Omdat herinneringen de levensduur van een enkele activering overleven, moeten ze expliciet worden geannuleerd (in tegenstelling tot verwijdering). U annuleert een herinnering door te bellen Grain.UnregisterReminder:
protected Task UnregisterReminder(IGrainReminder reminder)
Het reminder
is het handle-object dat wordt geretourneerd door Grain.RegisterOrUpdateReminder.
Exemplaren van IGrainReminder
zijn niet gegarandeerd geldig na de levensduur van een activering. Als u een herinnering wilt identificeren op een manier die zich blijft voordoen, gebruikt u een tekenreeks met de naam van de herinnering.
Als u alleen de naam van de herinnering hebt en het bijbehorende exemplaar nodig IGrainReminder
hebt, roept u de Grain.GetReminder methode aan:
protected Task<IGrainReminder> GetReminder(string reminderName)
Bepalen welke te gebruiken
U wordt aangeraden timers in de volgende omstandigheden te gebruiken:
- Als het niet uitmaakt (of wenselijk is) dat de timer niet meer functioneert wanneer de activering wordt gedeactiveerd of als er fouten optreden.
- De resolutie van de timer is klein (bijvoorbeeld redelijk uit te drukken in seconden of minuten).
- De callback van de timer kan worden gestart vanaf Grain.OnActivateAsync() of wanneer een graanmethode wordt aangeroepen.
We raden u aan om herinneringen in de volgende omstandigheden te gebruiken:
- Wanneer het periodieke gedrag de activering en eventuele fouten moet overleven.
- Het uitvoeren van onregelmatige taken (bijvoorbeeld redelijk uit te drukken in minuten, uren of dagen).
Timers en herinneringen combineren
U kunt overwegen om een combinatie van herinneringen en timers te gebruiken om uw doel te bereiken. Als u bijvoorbeeld een timer nodig hebt met een kleine resolutie die moet overleven tijdens activeringen, kunt u een herinnering gebruiken die om de vijf minuten wordt uitgevoerd, waarvan het doel is om een korrel te activeren die een lokale timer opnieuw start die mogelijk verloren is gegaan vanwege deactivering.
POCO-graanregistraties
Als u een timer of herinnering wilt registreren met een POCO-graan, implementeert u de IGrainBase interface en injecteert u de ITimerRegistry of IReminderRegistry in de constructor van het graan.
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);
}
}
}
Met de voorgaande code wordt:
- Definieert een POCO-graan dat implementeert IGrainBase,
IPingGrain
en IDisposable. - Registreert een timer die elke 10 seconden wordt aangeroepen en begint 3 seconden na de registratie.
- Wanneer
Ping
wordt aangeroepen, registreert u een herinnering die elk uur wordt aangeroepen en begint u direct na de registratie. - De
Dispose
methode annuleert de herinnering als deze is geregistreerd.