Dela via


Timers och påminnelser

Körningen Orleans innehåller två mekanismer, som kallas timers och påminnelser, som gör det möjligt för utvecklaren att ange periodiskt beteende för korn.

Timers

Timers används för att skapa periodiska kornbeteenden som inte krävs för att sträcka sig över flera aktiveringar (instansiering av kornet). En timer är identisk med standardklassen .NET System.Threading.Timer . Dessutom omfattas timers av entrådiga körningsgarantier inom kornaktiveringen som de arbetar med, och deras körningar interleaveds med andra begäranden, som om timerns återanrop var en kornmetod markerad med AlwaysInterleaveAttribute.

Varje aktivering kan ha noll eller fler timers associerade med den. Körningen kör varje timerrutin inom körningskontexten för aktiveringen som den är associerad med.

Tidsinställd användning

Om du vill starta en timer använder du Grain.RegisterTimer metoden som returnerar en IDisposable referens:

protected IDisposable RegisterTimer(
    Func<object, Task> asyncCallback, // function invoked when the timer ticks
    object state,                     // object to pass to asyncCallback
    TimeSpan dueTime,                 // time to wait before the first timer tick
    TimeSpan period)                  // the period of the timer

Om du vill avbryta timern tar du bort den.

En timer upphör att utlösas om kornet inaktiveras eller när ett fel inträffar och dess silo kraschar.

Viktiga överväganden:

  • När aktiveringssamlingen är aktiverad ändrar körningen av ett timeråteranrop inte aktiveringens tillstånd från inaktivt till i bruk. Det innebär att en timer inte kan användas för att skjuta upp inaktiveringen av annars inaktiva aktiveringar.
  • Perioden som skickas till Grain.RegisterTimer är den tid som passerar från det ögonblick då aktiviteten som returneras av asyncCallback matchas till det ögonblick då nästa anrop asyncCallback ska ske. Detta gör det inte bara omöjligt för efterföljande anrop att asyncCallback överlappa, utan gör det också så att den tid asyncCallback det tar att slutföra påverkar den frekvens med vilken asyncCallback anropas. Detta är en viktig avvikelse från semantiken System.Threading.Timeri .
  • Varje anrop av asyncCallback levereras till en aktivering på en separat tur och körs aldrig samtidigt med andra aktiverar samma aktivering. Anrop levereras dock asyncCallback inte som meddelanden och omfattas därför inte av semantik för mellanlagring av meddelanden. Det innebär att anrop av asyncCallback beter sig som om kornet återaktiveras och körs samtidigt med andra kornbegäranden. För att kunna använda kornets semantik för schemaläggning av begäranden kan du anropa en kornmetod för att utföra det arbete som du skulle ha gjort inom asyncCallback. Ett annat alternativ är att använda en AsyncLock eller en SemaphoreSlim. En mer detaljerad förklaring finns i Orleans GitHub-problem #2574.

Påminnelser

Påminnelser liknar timers, med några viktiga skillnader:

  • Påminnelser är beständiga och fortsätter att utlösas i nästan alla situationer (inklusive partiella eller fullständiga klusteromstarter) om de inte uttryckligen avbryts.
  • Påminnelse om att "definitioner" skrivs till lagring. Varje specifik förekomst, med sin specifika tid, är dock inte det. Detta har sidoeffekten att om klustret är nere vid tidpunkten för en specifik påminnelse-tick, kommer det att missas och endast nästa tick i påminnelsen sker.
  • Påminnelser är associerade med ett korn, inte någon specifik aktivering.
  • Om ett korn inte har någon aktivering associerad med den när en påminnelse tickar skapas kornet. Om en aktivering blir inaktiv och inaktiveras, reaktivaterar en påminnelse som är associerad med samma korn korn korn när den tickar nästa.
  • Påminnelseleverans sker via meddelande och omfattas av samma interleaving-semantik som alla andra kornmetoder.
  • Påminnelser ska inte användas för högfrekventa timers– deras period ska mätas i minuter, timmar eller dagar.

Konfiguration

Påminnelser, som är beständiga, förlitar sig på att lagringen fungerar. Du måste ange vilken lagringsbackning som ska användas innan undersystemfunktionerna påminnelse. Detta görs genom att konfigurera en av påminnelseprovidrar via Use{X}ReminderService tilläggsmetoder, där X är namnet på providern, till exempel UseAzureTableReminderService.

Azure Table-konfiguration:

// 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();

Om du bara vill att en platshållarimplementering av påminnelser ska fungera utan att behöva konfigurera ett Azure-konto eller EN SQL-databas får du en implementering endast för utveckling av påminnelsesystemet:

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

Påminnelseanvändning

Ett korn som använder påminnelser måste implementera IRemindable.ReceiveReminder metoden.

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

Om du vill starta en påminnelse använder du Grain.RegisterOrUpdateReminder metoden som returnerar ett IGrainReminder objekt:

protected Task<IGrainReminder> RegisterOrUpdateReminder(
    string reminderName,
    TimeSpan dueTime,
    TimeSpan period)
  • reminderName: är en sträng som unikt måste identifiera påminnelsen inom omfånget för det kontextuella kornet.
  • dueTime: anger hur mycket tid det tar att vänta innan den första timern skickas.
  • period: anger tidsperioden för timern.

Eftersom påminnelser överlever livslängden för en enskild aktivering måste de uttryckligen avbrytas (i stället för att tas bort). Du avbryter en påminnelse genom att ringa Grain.UnregisterReminder:

protected Task UnregisterReminder(IGrainReminder reminder)

reminder är det referensobjekt som returneras av Grain.RegisterOrUpdateReminder.

Instanser av IGrainReminder är inte garanterade att vara giltiga utöver livslängden för en aktivering. Om du vill identifiera en påminnelse på ett sätt som bevaras använder du en sträng som innehåller påminnelsens namn.

Om du bara har påminnelsens namn och behöver motsvarande instans av IGrainReminderanropar Grain.GetReminder du metoden:

protected Task<IGrainReminder> GetReminder(string reminderName)

Bestäm vilken som ska användas

Vi rekommenderar att du använder timers under följande omständigheter:

  • Om det inte spelar någon roll (eller är önskvärt) att timern upphör att fungera när aktiveringen inaktiveras eller om fel inträffar.
  • Timerns upplösning är liten (till exempel relativt uttrycksbar i sekunder eller minuter).
  • Timerns motringning kan startas från Grain.OnActivateAsync() eller när en kornmetod anropas.

Vi rekommenderar att du använder påminnelser under följande omständigheter:

  • När det periodiska beteendet behöver överleva aktiveringen och eventuella fel.
  • Utföra ovanliga uppgifter (till exempel rimligt uttrycksbara i minuter, timmar eller dagar).

Kombinera timers och påminnelser

Du kan överväga att använda en kombination av påminnelser och timers för att uppnå ditt mål. Om du till exempel behöver en timer med en liten upplösning som behöver överleva mellan aktiveringar kan du använda en påminnelse som körs var femte minut, vars syfte är att väcka ett korn som startar om en lokal timer som kan ha gått förlorad på grund av inaktivering.

POCO-kornregistreringar

Om du vill registrera en timer eller påminnelse med poco-korn implementerar IGrainBase du gränssnittet och matar ITimerRegistry in eller IReminderRegistry i kornkonstruktorn.

using Orleans.Runtime;
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.RegisterTimer(
            grainContext,
            asyncCallback: static async state =>
            {
                // Omitted for brevity...
                // Use state

                await Task.CompletedTask;
            },
            state: this,
            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);
        }
    }
}

Koden ovan:

  • Definierar ett POCO-korn som implementerar IGrainBase, IPingGrainoch IDisposable.
  • Registrerar en timer som anropas var 10:e sekund och startar 3 sekunder efter registreringen.
  • När Ping anropas registrerar du en påminnelse som anropas varje timme och börjar omedelbart efter registreringen.
  • Metoden Dispose avbryter påminnelsen om den är registrerad.