Dajbych.net


Azure Service Fabric a naplánované úlohy

, 5 minut čtení

service fabric logo

Naplánované úlohy mají mnoho názvů. Ve Windows se tradičně nazývá Plánovač úloh. V unixových prostředích se plánovač úloh nazývá Cron daemon. Microsoft Azure obsahuje Azure Scheduler a Azure Web Apps mají WebJobs. Service Fabric má svůj vlastní mechanismus nazvaný Actor Reminder. V tomto článku se dozvíte, jak je implementovat. V jedné sestavě lze zapouzdřit více úloh.

Rychlý přehled služby Actor

Cluster Service Fabric může hostovat více aplikací. Jedna aplikace obsahuje jednu nebo více služeb. Aplikace může kombinovat několik typů služeb. Může se například skládat z jedné bezstavové služby, dvou stavových služeb a deseti aktérů. Naplánované úlohy v Service Fabric jsou postavené na modelu Actor.

Objekt actor je třída s trvalým stavem nebo bez něj. Objekt actor je volán prostřednictvím svého proxy serveru. Serializace dat se provádí automaticky. Jeden aktér má nanejvýš jedno vlákno. Všechna volání jednoho objektu actor jsou zařazena do fronty. Objekt actor se škáluje na základě rozsahu jeho identifikátoru. Když volající získá proxy server nějakého objektu actor, musí poskytnout identifikátor objektu actor. Pokud objekt actor s daným identifikátorem neexistuje, vytvoří se jeho instance. Každá instance objektu actor má svůj vlastní stav. Pokud se instance nepoužívá po dobu jedné hodiny (výchozí interval), instance se shromažďuje z odpadu.

Vytvoření nového projektu

Start Page → Create new project… → Installed → Templates → Visual C# → Cloud → Service Fabric Application → OK → Actor Service → OK

IActor1.cs:

public interface IActor1 : IActor {
    
    Task RegisterReminder();
    
}

To je rozhraní Actor1. Objekt actor musí být volán pouze prostřednictvím svého rozhraní, které je implementováno proxy serverem objektu actor. Ustanovení Actor1 poskytuje pouze jednu metodu nastavení časovače.

Actor1.cs:

[StatePersistence(StatePersistence.None)]
internal class Actor1 : Actor, IActor1, IRemindable {

    public Actor1(ActorService actorService, ActorId actorId) : base(actorService, actorId) { }

    public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) {
        var location = Directory.GetCurrentDirectory();
        var current = DateTime.Now;
        Thread.Sleep(2 * 60 * 1000);
        using (var writer = File.AppendText("actor.txt")) {
            await writer.WriteLineAsync("1 :: " + current.ToString() + " --> " + DateTime.Now.ToString());
        }
    }

    public async Task RegisterReminder() {
        try {
            var previousRegistration = GetReminder("Reminder1");
            await UnregisterReminderAsync(previousRegistration);
        } catch (ReminderNotFoundException) { }

        var reminderRegistration = await RegisterReminderAsync("Reminder1", null, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(1));
    }

}

Implementace třídy Actor1 obsahuje logiku naplánované úlohy. Když připomenutí zaškrtne, vyvolá metodu ReceiveReminderAsync prostřednictvím rozhraní IRemindable.

V tomto příkladu metoda zapisuje počáteční a koncový čas svého provádění do souboru v pracovním adresáři. Po volání Directory.GetCurrentDirectory() můžete přepnout zarážku a zobrazit umístění souboru. Umístění souboru se mění při každém nasazení do clusteru (což není upgrade).

Metoda RegisterReminder je zodpovědná za plánování (nebo přeplánování) připomenutí s názvem Reminder1. Připomenutí se zaškrtne, pokud je objekt actor uvolněn z paměti (vytvoří se nová instance). Časovač běží pouze před uvolněním paměti. Časovač se zastaví spolu s uvolňováním paměti.

ScheduledActorService.cs:

internal class ScheduledActorService<T> : ActorService where T : IActor {

    public ScheduledActorService(StatefulServiceContext context, ActorTypeInformation actorType) : base(context, actorType) { }

    protected async override Task RunAsync(CancellationToken cancellationToken) {
        await base.RunAsync(cancellationToken);

        var proxy = ActorProxy.Create<T>(new ActorId(0));

        switch (proxy) {
            case IActor1 a1:
                await a1.RegisterReminder();
                break;
            case IActor2 a2:
                await a2.RegisterReminder();
                break;
            default:
                throw new NotImplementedException($"{GetType().FullName}.{nameof(RunAsync)}");
        }
    }

}

Tato třída je v tomto příkladu nejsložitější. Metoda RunAsync se spustí při každém spuštění primární repliky služby actor (po převzetí služeb při selhání, vyrovnávání prostředků, upgradu aplikace atd.). Naplánuje připomenutí při prvním spuštění služby nebo přeplánuje připomenutí po každém upgradu (interval připomenutí se může v nové verzi lišit). Neodebírejte volání metody base.RunAsync.

Ustanovení switch je nezbytné, protože objekt actor musí implementovat rozhraní, které je zděděno přímo z IActor. V opačném případě bychom mohli metodu RegisterReminder umístit do samostatného rozhraní mezi IActor a IActor1. V současné době není podporována žádná hierarchie rozhraní. Porušení tohoto pravidla přeruší sestavení.

Program.cs:

internal static class Program {
    private static void Main() {
        try {
            ActorRuntime.RegisterActorAsync<Actor1>((context, actorType) => new ScheduledActorService<IActor1>(context, actorType)).Wait();
            ActorRuntime.RegisterActorAsync<Actor2>((context, actorType) => new ScheduledActorService<IActor2>(context, actorType)).Wait();

            Thread.Sleep(Timeout.Infinite);
        } catch {
            throw;
        }
    }
}

Metoda Main registruje aktéry s třídou ScheduledActorService<T>, která je zodpovědná za registraci připomenutí. Třída musí být deklarována s odpovídajícím rozhraním objektu actor.

Můžete experimentovat s intervaly připomenutí a trváním provádění a odpovědět na několik otázek týkajících se chování připomenutí.