Класс BackgroundService, работающий в контейнере Docker Linux, не завершает работу корректно - PullRequest
3 голосов
/ 11 октября 2019

Я создал рабочий сервис, унаследованный от Microsoft.Extensions.Hosting.BackgroundService, который я затем развернул в докерном контейнере Linux на моем компьютере с помощью отладчика Visual Studio. Я ставлю точку останова на код, который происходит, когда cancellationtoken.IsCancellationRequested имеет значение true. Затем я запускаю «docker stop --time = 30» для контейнера, точка останова никогда не срабатывает, и через 30 секунд отладчик принудительно останавливается.

Я также попытался переопределить метод StopAsync и установить там точку останова, которая также не вызывается. Я использую .net core 3, последнюю версию рабочего стола Docker. Я подтвердил, что StartAsync вызывается.

Это мой программный файл.

 public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }


        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                        .AddEnvironmentVariables().Build();
                })
                .ConfigureServices((hostContext, services) => { services.AddHostedService<Worker>();    });

    }

Если у кого-то есть идея о том, что я пропустил, или рабочий пример службы без веб-сервера, которая уважает остановку, я был бы очень признателен.

Добавление того, как выглядит мой работник:

using System;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace MyWorker
{
    public class Worker : BackgroundService
    {    
        public Worker(ILogger<Worker> logger, IConfiguration configs)
        {

        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
           return base.StopAsync(cancellationToken);   
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        { 
            return base.StartAsync(cancellationToken);
        }


        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            try
            {
                await DoWork(cancellationToken); //While loop that checks token in here
            }

            catch (Exception ex)
            {

                throw;
            }
        }
    }
}

Ответы [ 2 ]

1 голос
/ 14 октября 2019

Для прослушивания Ctrl + C, SIGINT и SIGTERM вам необходимо добавить Поддержка консольной жизни , либо через UseConsoleLifetime () или RunConsoleAsync , например:

public static async Task Main(string[] args)
{
    CreateHostBuilder(args).Build().RunConsoleAsync();
}

или

public static void Main(string[] args)
{
    CreateHostBuilder(args).UseConsoleLifeTime().Build().Run();
}

Если выключение занимает больше времени, установленного в ShutdownTimeout , регистрируется предупреждение.

Как это работает

Знание того, как работает время жизни консоли, может помочь в устранении задержек.

Если вы проверите источник, RunConsoleAsync - это не что иное, как вызов UseConsoleLifeTime().Build().RunAsync();

Внутренний ConsoleLifetime класс добавляет прослушиватели событий для событий Cancel и ProcessExit:

AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
Console.CancelKeyPress += OnCancelKeyPress;

Ctrl + C только перенаправит запрос на остановку на хост :

private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
    e.Cancel = true;
    ApplicationLifetime.StopApplication();
}

С другой стороны, обработчик ProcessExit выдаст предупреждение, если завершение работы займет больше времени, чем указано в HostOptions.ShutdownTimeout :

private void OnProcessExit(object sender, EventArgs e)
{
    ApplicationLifetime.StopApplication();
    if(!_shutdownBlock.WaitOne(HostOptions.ShutdownTimeout))
    {
        Logger.LogInformation("Waiting for the host to be disposed. Ensure all 'IHost' instances are wrapped in 'using' blocks.");
    }
    _shutdownBlock.WaitOne();
    // On Linux if the shutdown is triggered by SIGTERM then that's signaled with the 143 exit code.
    // Suppress that since we shut down gracefully. https://github.com/aspnet/AspNetCore/issues/6526
    System.Environment.ExitCode = 0;
}
0 голосов
/ 22 октября 2019

Ну, через 4 дня я наконец понял, проблема не в коде или в докере, проблема в отладчике Visual Studio. Если вы запускаете свой код нажатием кнопки воспроизведения в VS2019, он не будет соответствовать SIGTERM. Если вы выполните ручную команду запуска Docker, вы увидите, что ваш код реагирует на команду DOCKER STOP.

Возможно, в конце концов можно будет заставить его работать, но я не нашел его.

...