J'ai une application de console (DotNet Core, Ubuntu), qui ressemble à la suivante:
void Main()
{
try
{
var job = new Job();
job.Start();
while(Console.ReadLine() != "quit");
}
catch(Exception e)
{
//some handling
}
}
Tandis que Job
est la mise en œuvre de JobAbstract
classe:
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
est défini comme:
public class Job : JobAbstract
{
public Job(periodMs) : base(periodMs)
{}
public override async Task Execute()
{
await SomeTask();
OtherKindOfJob();
await MaybeMoreAsyncTasks();
// etc
}
}
Tout fonctionne bien, comme vous pouvez vous attendre.
Maintenant, je l'enveloppe dans des conteneurs Docker pour une livraison continue. docker stop
Peut être exécuté sur un conteneur pendant que Execute
méthode de Job
est exécuté. Afin d'attendre que le cycle se termine puis sortez gracieusement, j'ai décidé d'utiliser cette approche:
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();
}
Je l'ai testé et cela fonctionne, lorsque vous appelez docker stop
, Docker Daemon envoie SIGTERM
signal sur le processus n ° 1 du conteneur (qui se trouve être mon application) et CORECLR invoque AssemblyLoadContext.Default.Unloading
Événement qui est traité de manière appropriée.
J'ai donc une application de travail et une manière (théoriquement) comment l'arrêter. Je ne suis tout simplement pas sûr, comment dois-je le mettre en œuvre pour un contexte donné. Devrais-je envoyer une sorte de jeton au Job
? Je veux que le flux de course soit comme suit:
SIGTERM
est reçu. Si Execute
_ ne fonctionne pas, arrêtez l'application, si c'est-à-dire, attendez que cela se termine.Execute
prend fin, terminez l'application.Comment ce genre de chose devrait être mis en œuvre? S'il vous plaît conseiller une sorte de schéma ou de modèle pour y parvenir. Merci d'avance!
Donc, je pensais quelque chose dehors: que si j'ajoute public Task CurrentIteration { get; private set; }
Et changer Execute();
à:
CurrentIteration = Execute();
await CurrentIteration;
et après starting.Wait();
Dans l'échantillon d'arrêt, ajoutez:
job.Stop();
await job.CurrentIteration;
Ça marchera? Y a-t-il des problèmes avec l'approche suivante?
En regardant votre code, je suppose que toute la logique de votre application peut être exprimée comme une seule Task Execute()
. Et ce dont vous avez besoin est assez simple annulation de tâche schéma.
Votre code pourrait ressembler à:
public static void Main(string[] args)
{
Start();
}
public static async void Start()
{
var cts = new CancellationTokenSource();
var starting = new ManualResetEventSlim();
AssemblyLoadContext.Default.Unloading += ctx =>
{
System.Console.WriteLine("Unloding fired");
cts.RequestCancellation();
System.Console.WriteLine("Waiting for completion");
ended.Wait();
};
// run application
try
{
await Execute(cts.Token);
}
catch(TaskCancellationException ex)
{
// cancelled
}
ended.Set();
}
Ce que je n'aime pas, c'est comment vous gérez le "signal" d'arrêt du docker. Je n'aime pas que vous avez essentiellement une "impasse" "l'événement Unloading
jusqu'à ce que l'application arrive. Mais je ne sais pas s'il y a une autre façon que je n'ai pas d'expérience avec Docker et que je n'ai jamais eu à gérer l'arrêt "gracieux" lorsque l'application est terminée en utilisant SIGTERM
. EDIT: REGARDER - Ceci , votre approche semble être la seule façon de le gérer. Il y a même un gars qui fait de la même manière que je suggère.