Freigeben über


Timer und Erinnerungen

Die Orleans Laufzeit bietet zwei Mechanismen, Zeitgeber und Erinnerungen, mit denen Sie regelmäßiges Verhalten für Getreide angeben können.

Timer

Verwenden Sie Zeitgeber , um regelmäßiges Kornverhalten zu erstellen, das nicht erforderlich ist, um mehrere Aktivierungen (Instanziierungen des Korns) zu umfassen. Ein Zeitgeber ist identisch mit der Standard-System.Threading.Timer-.NET-Klasse. Darüber hinaus unterliegen Timer den Einzelthreadausführungsgarantien innerhalb der Grain-Aktivierung, in der sie ausgeführt werden.

Jede Aktivierung kann keine oder mehrere Zeitgeber zugeordnet haben. Die Laufzeit führt jede Timerroutine innerhalb des Laufzeitkontexts der zugehörigen Aktivierung aus.

Verwendung des Zeitgebers

Verwenden Sie zum Starten eines Zeitgebers die RegisterGrainTimer-Methode, die einen IGrainTimer-Verweis zurückgibt:

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

Um den Timer abzubrechen, löschen Sie ihn.

Ein Timer wird nicht mehr ausgelöst, wenn das Getreide deaktiviert wird oder wenn ein Fehler auftritt und sein Silo abstürzt.

Wichtige Überlegungen:

  • Wenn die Aktivierungsauflistung aktiviert ist, ändert das Ausführen eines Timerrückrufs nicht den Status der Aktivierung von "Leerlauf" in "In-Verwendung". Dies bedeutet, dass Sie keinen Timer verwenden können, um die Deaktivierung anderer Leerlaufaktivierungen zu verschieben.
  • Der Zeitraum, der an Grain.RegisterGrainTimer übergeben wird, ist die Zeitspanne vom Moment, in dem der von Task zurückgegebene callback aufgelöst wird, bis zum Zeitpunkt, an dem der nächste Aufruf von callback erfolgen soll. Dies verhindert nicht nur, dass sich aufeinanderfolgende Aufrufe von callback überlappen, sondern bedeutet auch, dass die Zeit, die callback benötigt, um abgeschlossen zu werden, die Häufigkeit beeinflusst, mit der callback aufgerufen wird. Dies ist eine wichtige Abweichung von der Semantik der System.Threading.Timer.
  • Jeder Aufruf von callback wird an eine Aktivierung auf einer separaten Runde gesendet und nie gleichzeitig mit anderen Runden innerhalb derselben Aktivierung ausgeführt.
  • Rückrufe werden standardmäßig nicht interleave. Sie können die Verschachtelung aktivieren, indem Sie Interleave auf trueGrainTimerCreationOptions festlegen.
  • Sie können Korntimer mithilfe der Change(TimeSpan, TimeSpan) Methode für die zurückgegebene IGrainTimer Instanz aktualisieren.
  • Callbacks können das Grain aktiv halten und das Einsammeln verhindern, wenn das Timerintervall relativ kurz ist. Aktivieren Sie dies, indem Sie KeepAlive auf true bei GrainTimerCreationOptions festlegen.
  • Rückruf-Funktionen können ein CancellationToken erhalten, das abgebrochen wird, wenn der Timer verworfen wird oder das Grain mit der Deaktivierung beginnt.
  • Rückrufe können den Korntimer löschen, der sie ausgelöst hat.
  • Rückrufe unterliegen Kornaufruffiltern.
  • Rückrufe sind in der verteilten Ablaufverfolgung sichtbar, wenn die verteilte Ablaufverfolgung aktiviert ist.
  • POCO-Entitäten (Kornklassen, die nicht von Grain erben) können Korntimer mithilfe der RegisterGrainTimer Erweiterungsmethode registrieren.

Mahnungen

Erinnerungen ähneln Zeitgebern, mit einigen wichtigen Unterschieden:

  • Erinnerungen sind beständig und werden in fast allen Situationen ausgelöst (einschließlich teilweiser oder vollständiger Clusterneustarts), sofern sie nicht ausdrücklich abgebrochen werden.
  • „Erinnerungsdefinitionen“ werden in den Speicher geschrieben. Jedes bestimmte Vorkommen mit seiner bestimmten Zeit wird jedoch nicht gespeichert. Dies hat den Nebeneffekt, dass, wenn der Cluster außer Betrieb ist, wenn ein bestimmtes Erinnerungsintervall fällig ist, er verpasst wird, und nur der nächste Erinnerungstakt auftritt.
  • Erinnerungen sind mit einem Grain verknüpft, nicht mit einer bestimmten Aktivierung.
  • Wenn ein Grain keine Aktivierung zugeordnet hat, wenn eine Erinnerung ausgelöst wird, erstellt Orleans die Grain-Aktivierung. Wenn eine Aktivierung im Leerlauf ist und deaktiviert wird, reaktiviert eine Erinnerung, die mit demselben Grain verknüpft ist, das Grain, wenn sie das nächste Mal ausgelöst wird.
  • Die Erinnerungsübermittlung erfolgt über eine Nachricht und unterliegt der gleichen Interleavingsemantik wie alle anderen Grainmethoden.
  • Sie sollten keine Erinnerungen für häufig verwendete Timer verwenden; deren Zeitraum sollte in Minuten, Stunden oder Tagen gemessen werden.

Konfiguration

Da Erinnerungen dauerhaft sind, sind sie auf Speicher angewiesen, um zu funktionieren. Sie müssen angeben, welche Speichersicherung verwendet werden soll, bevor das Erinnerungssubsystem funktionieren kann. Konfigurieren Sie dazu einen der Erinnerungsanbieter über Use{X}ReminderService Erweiterungsmethoden, wobei X es sich um den Namen des Anbieters handelt (z. B. 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();

Wenn Sie nur möchten, dass eine Platzhalterimplementierung von Erinnerungen funktioniert, ohne ein Azure-Konto oder eine SQL-Datenbank einrichten zu müssen, bietet dies eine entwicklungsgeschützte Implementierung des Erinnerungssystems:

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

Wichtig

Wenn Sie einen heterogenen Cluster haben, in dem die Silos verschiedene Graintypen verarbeiten (unterschiedliche Schnittstellen implementieren), muss jedes Silo die Konfiguration für Erinnerungen hinzufügen, auch wenn das Silo selbst keine Erinnerungen verarbeitet.

Verwendung von Erinnerungen

Ein Korn mit Erinnerungshilfen muss die IRemindable.ReceiveReminder-Methode implementieren.

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

Verwenden Sie zum Starten einer Erinnerung die Grain.RegisterOrUpdateReminder-Methode, die ein IGrainReminder-Objekt zurückgibt:

protected Task<IGrainReminder> RegisterOrUpdateReminder(
    string reminderName,
    TimeSpan dueTime,
    TimeSpan period)
  • reminderName: ist eine Zeichenfolge, die die Erinnerung im Rahmen des Contextual Grains eindeutig identifizieren muss.
  • dueTime: Gibt eine Zeitspanne an, die gewartet werden soll, bevor der erste Timer-Tick ausgegeben wird.
  • period: gibt die Dauer des Timers an.

Da Erinnerungen die Lebensdauer einer einzelnen Aktivierung überleben, müssen Sie sie explizit abbrechen (im Gegensatz zur Löschung). Erinnerung stornieren, indem Sie Grain.UnregisterReminder aufrufen:

protected Task UnregisterReminder(IGrainReminder reminder)

Das reminder ist das Handleobjekt, das von Grain.RegisterOrUpdateReminder zurückgegeben wird.

Bei Instanzen von IGrainReminder ist nicht garantiert, dass sie über die Lebensdauer einer Aktivierung hinaus gültig sind. Wenn Sie eine Erinnerung dauerhaft identifizieren möchten, verwenden Sie eine Zeichenfolge, die den Namen der Erinnerung enthält.

Wenn Sie nur den Namen der Erinnerung haben und die entsprechende IGrainReminder Instanz benötigen, dann rufen Sie die Grain.GetReminder Methode auf:

protected Task<IGrainReminder> GetReminder(string reminderName)

Entscheidung über die Verwendung

Es wird empfohlen, Timer unter folgenden Umständen zu verwenden:

  • Wenn es egal (oder wünschenswert) ist, dass der Timer nicht mehr funktioniert, wenn die Aktivierung deaktiviert oder Fehlfunktionen auftreten.
  • Die Auflösung des Timers ist klein (z. B. sinnvoll in Sekunden oder Minuten darstellbar).
  • Sie können den Timer-Callback von Grain.OnActivateAsync() oder wenn eine Grain-Methode aufgerufen wird, starten.

Wir empfehlen die Verwendung von Erinnerungen unter folgenden Umständen:

  • Wenn das regelmäßige Verhalten auch nach der Aktivierung und bei eventuell auftretenden Fehlern bestehen bleiben soll.
  • Ausführen seltener Aufgaben (z. B. angemessen darstellbar in Minuten, Stunden oder Tagen).

Kombinieren von Zeitgebern und Erinnerungen

Sie können eine Kombination aus Erinnerungen und Zeitgebern verwenden, um Ihr Ziel zu erreichen. Wenn Sie beispielsweise einen Timer mit einer kleinen Auflösung benötigen, die über Aktivierungen hinweg überleben muss, können Sie eine Erinnerung verwenden, die alle fünf Minuten ausgeführt wird. Ihr Zweck wäre es, ein Korn aufzuwachen, das einen lokalen Timer neu startet, der aufgrund der Deaktivierung möglicherweise verloren ging.

POCO-Grainregistrierungen

Um einen Timer oder eine Erinnerung mit einem POCO Grain zu registrieren, implementieren Sie das -Interface und injizieren IGrainBase oder ITimerRegistry in den Konstruktor des Grains.

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);
        }
    }
}

Der vorangehende Code führt folgende Aktionen aus:

  • Definiert ein POCO-Objekt, das IGrainBase, IPingGrain und IDisposable implementiert.
  • Registriert einen Timer, der alle 10 Sekunden aufgerufen wird, beginnend 3 Sekunden nach der Registrierung.
  • Wenn Ping aufgerufen wird, wird eine Erinnerung registriert, die jede Stunde aufgerufen wird und unmittelbar nach der Registrierung beginnt.
  • Die Dispose-Methode bricht die Erinnerung ab, wenn sie registriert ist