工人服务意外停止工作

Worker service stops working unexpectedly

我有 .NET Core 3+ 工作者服务,每 10 秒检查一次“一些东西”。有一次,它“随机地”停止这样做,我不确定为什么。到目前为止它发生了两次并且没有异常日志或类似的东西,所以我只能假设我应该在 ExecuteAsync 中添加一个 try-catch,但我的问题是:是否存在任何可能导致类似行为的已知问题工人刚刚停止执行的地方?

是的,没有取消请求(否则,我想它会记录消息“发件人正在停止”)。

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Sender is starting");
        stoppingToken.Register(() => _logger.LogInformation("Sender is stopping"));

        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(10000, stoppingToken);

            _logger.LogDebug("Worker running at: {time}", DateTimeOffset.Now);

            if (!stoppingToken.IsCancellationRequested)
                await SendPendingMessages();
        }
    }

    private async Task SendPendingMessages()
    {
        var list = ...

        foreach (var item in list)
        {
            try
            {
                // Do stuff as expected
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, ex.Message);
            }
        }
    }
}

请注意,行

await Task.Delay(10000, stoppingToken);

当您触发取消令牌时将抛出 OperationCancelledException,它未处理。

还要确保 LogError() LogDebug() 有一个 catch all tr​​y 块并且不能抛出它们自己,这也会破坏你的代码。

看不到任何其他问题。

So far it happened twice and there are no exception logs or anything like it, so I can only assume that I should add a try-catch inside ExecuteAsync, but my question is: Are there any known issues that could cause a similar behavior where the worker just stops executing?

这对我来说绝对是个例外。默认情况下,the BackgroundService implementation will ignore any exceptions thrown from ExecuteAsync。如果从 ExecuteAsync 引发异常,它会被忽略并且服务会停止 运行.

我总是推荐带有日志记录的顶级 try/catch,这样您至少知道会发生这种情况:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
  _logger.LogInformation("Sender is starting");
  try
  {
    while (!stoppingToken.IsCancellationRequested)
    {
      await Task.Delay(10000, stoppingToken);
      _logger.LogDebug("Worker running at: {time}", DateTimeOffset.Now);
      if (!stoppingToken.IsCancellationRequested)
        await SendPendingMessages();
    }
  }
  catch (Exception ex) when (stoppingToken.IsCancellationRequested)
  {
    _logger.LogInformation("Sender is stopping");
  }
  catch (Exception ex) when (!stoppingToken.IsCancellationRequested)
  {
    _logger.LogError(ex, "Sender had error");
  }
}

相关说明,如果这是您认为的“关键”后台服务,那么您还需要 stop the application host when this service stops:

private readonly ILogger<Worker> _logger;
private readonly IHostApplicationLifetime _hostApplicationLifetime;

public Worker(ILogger<Worker> logger, IHostApplicationLifetime hostApplicationLifetime)
{
  _logger = logger;
  _hostApplicationLifetime = hostApplicationLifetime;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
  _logger.LogInformation("Sender is starting");
  try
  {
    while (!stoppingToken.IsCancellationRequested)
    {
      await Task.Delay(10000, stoppingToken);
      _logger.LogDebug("Worker running at: {time}", DateTimeOffset.Now);
      if (!stoppingToken.IsCancellationRequested)
        await SendPendingMessages();
    }
  }
  catch (Exception ex) when (stoppingToken.IsCancellationRequested)
  {
    _logger.LogInformation("Sender is stopping");
  }
  catch (Exception ex) when (!stoppingToken.IsCancellationRequested)
  {
    _logger.LogError(ex, "Sender had error");
  }
  finally
  {
    _hostApplicationLifetime.StopApplication();
  }
}

https://www.thecodebuzz.com/waiting-for-host-disposed-ensure-ihost-instances-wrapped-in-using-blocks/

enter image description here

UseConsoleLifetime() 有帮助:示例如下

return Host.CreateDefaultBuilder(args)
            .ConfigureServices((_, services) =>
            {

                services.AddHttpClient();

            }).UseConsoleLifetime();