Ho un'applicazione console (dotnet core, ubuntu), che assomiglia al seguente:
void Main()
{
try
{
var job = new Job();
job.Start();
while(Console.ReadLine() != "quit");
}
catch(Exception e)
{
//some handling
}
}
Mentre Job
è l'implementazione di JobAbstract
class:
public class JobAbstract
{
private readonly int _periodMs;
protected JobAbstract(int periodMs)
{
_periodMs = periodMs;
}
public bool Working { get; private set; }
public abstract Task Execute();
private void LogFatalError(Exception exception)
{
try
{
//logging
}
catch (Exception)
{
}
}
private async Task ThreadMethod()
{
while (Working)
{
try
{
await Execute();
}
catch (Exception exception)
{
LogFatalError(exception);
}
await Task.Delay(_periodMs);
}
}
public virtual void Start()
{
if (Working)
return;
Working = true;
Task.Run(async () => { await ThreadMethod(); });
}
public void Stop()
{
Working = false;
}
}
Job
è definito come:
public class Job : JobAbstract
{
public Job(periodMs) : base(periodMs)
{}
public override async Task Execute()
{
await SomeTask();
OtherKindOfJob();
await MaybeMoreAsyncTasks();
// etc
}
}
Funziona tutto bene, come ci si potrebbe aspettare.
Ora sto avvolgendo tutto in contenitori docker per la consegna continua. docker stop
può essere eseguito su un contenitore mentre viene eseguito il metodo Execute
di Job
. Per aspettare che il ciclo termini e poi uscire con grazia, ho deciso di utilizzare questo approccio:
public static void Main(string[] args)
{
var ended = new ManualResetEventSlim();
var starting = new ManualResetEventSlim();
AssemblyLoadContext.Default.Unloading += ctx =>
{
System.Console.WriteLine("Unloding fired");
starting.Set();
System.Console.WriteLine("Waiting for completion");
ended.Wait();
};
System.Console.WriteLine("Waiting for signals");
starting.Wait();
System.Console.WriteLine("Received signal gracefully shutting down");
Thread.Sleep(5000);
ended.Set();
}
L'ho provato e funziona, quando si chiama docker stop
, il daemon docker invia il segnale SIGTERM
al processo # 1 del contenitore (che è la mia app) e CoreCLR richiama l'evento AssemblyLoadContext.Default.Unloading
che è gestito in modo appropriato.
Quindi ho un'app funzionante e un modo (teoricamente) come fermarlo. Non sono sicuro, come dovrei implementarlo per un determinato contesto. Dovrei inviare qualche tipo di token al Job
? Voglio che il flusso di corsa sia come segue:
- L'app è in esecuzione normalmente.
-
SIGTERM
è ricevuto. SeExecute
non è in esecuzione, interrompi l'app, se è presente, attendi che termini. - Quando termina% co_de, termina l'app.
Come dovrebbe essere implementato questo genere di cose? Si prega di consigliare qualche tipo di schema o schema per raggiungere questo obiettivo. Grazie in anticipo!
Quindi ho capito qualcosa: cosa succede se aggiungo Execute
e cambio public Task CurrentIteration { get; private set; }
in:
CurrentIteration = Execute();
await CurrentIteration;
e dopo Execute();
nell'arresto dell'esempio, aggiungere:
job.Stop();
await job.CurrentIteration;
Funzionerà? Ci sono problemi con il seguente approccio?