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 Actor service
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
- Přesuňte IActor1.cs z projektu
Actor1.Interfaces
do projektuActor1
. - Odstraňte
Actor1.Interfaces
z řešení. Jeden herec bude zastupovat jednu naplánovanou úlohu. Úloha bude vyvolána interně, takže rozhraní nemusí být vystavena. - Otevřete rozhraní IActor1.cs a odeberte metody
SetCountAsync
aGetCountAsync
. Místo toho přidejte metoduTask RegisterReminder()
(viz kód níže). - Otevřete třídu Actor1.cs a změňte atribut
StatePersistence
zPersisted
naNone
. - Odeberte metody
SetCountAsync
aGetCountAsync
. - Implementujte rozhraní
IActor1
aIRemindable
(viz kód níže). - Vytvořte novou třídu
ScheduledActorService<T>
a implementujte ji podle následujícího příkladu. - V souboru Program.cs v metodě
Main
nahraďte tříduActorService
třídouScheduledActorService<T>
(viz kód níže). - Vytvořte kopii rozhraní
IActor1
a pojmenujte jiIActor2
. - Vytvořte kopii třídy
Actor1
a pojmenujte jiActor2
. VActor2
, v metoděRegisterReminder
, změňte všechny výskyty řetězce"Reminder1"
na"Reminder2"
.
IActor1.cs:
public interface IActor1 : IActor {
Task RegisterReminder();
}
Toto 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. Actor1
zpřístupňuje 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). Neodstraňujte 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í.
- Co se stane, když provádění úlohy trvá déle než je interval období?
MetodaReceiveReminderAsync
je očekávatelná z dobrého důvodu. Další tik nastane po dokončení provádění metody. - Co když hostitelský uzel selže nebo je třeba aktualizovat základní systémy?
Provádění služby je přerušeno a vytvořena instance na jiném uzlu. MetodaScheduledActorService
bude volat metoduRegisterReminder
pro každého registrovaného aktéra. - Co když chci spustit úlohu každý den v danou dobu?
MetodaRegisterReminderAsync
má parametrdueTime
. Dobu trvání k nejbližšímu výskytu je nutné určit na základě aktuálního času.