.NET 核心 BackgroundService 不会作为守护程序正常关闭 [英] .NET core BackgroundService does not shut down gracefully as daemon

查看:115
本文介绍了.NET 核心 BackgroundService 不会作为守护程序正常关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发 .NET Core 3.1 后台服务,该服务将作为守护程序安装在 Debian AWS EC2 实例上.
优雅地关闭守护进程以停止运行任务并完成一些要处理的任务(发送一些事件等)非常重要.基本实现如下所示:

使用 Microsoft.Extensions.DependencyInjection;使用 Microsoft.Extensions.Hosting;命名空间 MyApp.WorkerService{公开课计划{public static void Main(string[] args){CreateHostBuilder(args).UseSystemd().Build().Run();}公共静态 IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureServices((hostContext, services) =>{services.AddHostedService();});}}

你可以看到我在这里使用了 SystemdLifetime.

工人如下:

使用系统;使用 System.Threading;使用 System.Threading.Tasks;使用 AdClassifier.App.Audit;使用 Microsoft.Extensions.Configuration;使用 Microsoft.Extensions.DependencyInjection;使用 Microsoft.Extensions.Hosting;使用 NLog;命名空间 MyApp.WorkerService{公共类工人:BackgroundService{private static readonly ILogger Logger = LogManager.GetLogger(typeof(Worker).FullName);私有只读 int _jobPollIntervalMilliseconds;公共 IServiceProvider 服务 { 获取;}public Worker(IServiceProvider服务,IConfiguration配置){服务 = 服务;_jobPollIntervalMilliseconds = configuration.GetValue("JobPollIntervalMilliseconds");}受保护的覆盖任务 ExecuteAsync(CancellationToken 停止令牌){Logger.Info("工人正在运行.");var task = new Task(o => DoWork(stoppingToken),toppingToken);task.Start();返回任务;}公共覆盖异步任务 StopAsync(CancellationToken 取消令牌){Logger.Info("工人停止");等待 base.StopAsync(cancellationToken);Logger.Info("工人停止");}私有无效 DoWork(CancellationToken 停止令牌){while (!stopingToken.IsCancellationRequested){使用 (var scope = Services.CreateScope()){//做一些工作}Thread.Sleep(_jobPollIntervalMilliseconds);}Logger.Info("请求取消!");}}}

问题
正如我提到的,我们将它设置为守护进程,就像这样

[单位]描述=我的工人需要=deploy-my-worker.serviceAfter=multi-user.target deploy-my-worker.serviceConditionFileIsExecutable=/home/my-worker/current/myworker[服务]环境=DOTNET_PRINT_TELEMETRY_MESSAGE=false环境=DOTNET_CLI_TELEMETRY_OPTOUT=true环境=ASPNETCORE_URLS=http://*:5000环境=DOTNET_ENVIRONMENT=暂存环境=ASPNETCORE_ENVIRONMENT=暂存工作目录=/home/my-worker/currentExecStart=/home/my-worker/current/myworkerSyslogIdentifier=我的工人重启=总是重启秒=10KillSignal=SIGTERM用户=usr组=用户组[安装]WantedBy=multi-user.target

问题是worker不会优雅地停止.我正在检查以下日志条目的日志,但它们没有出现:
工人停止取消请求!工人停止
请注意,应用程序确实会关闭.我们尝试关闭服务的方法如下:

  • 关闭服务器
  • systemctl stop my-worker.service
  • kill
  • kill -SIGTERM
  • kill -SIGINT

什么有效

如果我这样启动 worker:usr@ip-10-168-19-126:~/my-worker/current$ ./myworker 然后按 Ctrl-C(应该是 SIGINT),应用程序停止,在我的日志中我可以看到正确的消息:

2020-05-21 16:16:57.9676|INFO|MyApp.WorkerService.Worker|工人停止2020-05-21 16:16:57.9937|INFO|MyApp.WorkerService.Worker|请求取消!2020-05-21 16:16:57.9937|INFO|MyApp.WorkerService.Worker|工人停止2020-05-21 16:16:57.9980 信息 AppDomain 关闭.记录器关闭...

有什么想法可以让守护进程按预期工作吗?

注意:
我有充分的理由相信问题出在守护进程设置的某个地方,或者 UseSystemd().
我将 UseSystemd() 替换为 UseWindowsService() 并将其安装为 Windows 机器上的 Windows 服务.然后继续通过服务"面板启动和停止服务,并按预期看到关闭日志记录.
所以,我很想假设在实现中没有问题,而是在设置中的某个地方.

解决方案

看来问题出在 NLog 的关闭上.这是通过执行以下操作修复的:LogManager.AutoShutdown = false; 并在 Worker::StopAsync 中添加 LogManager.Shutdown();

I am working on a .NET Core 3.1 background service to be installed as a daemon on an Debian AWS EC2 instance.
It is important to gracefully shut down the daemon to stop running tasks and finalize a number of tasks to be handled (sending some events, etc). The basic implementation looks like this:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });        
    }
}

You can see I am using the SystemdLifetime here.

The worker is as follows:

using System;
using System.Threading;
using System.Threading.Tasks;
using AdClassifier.App.Audit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NLog;

namespace MyApp.WorkerService
{
    public class Worker : BackgroundService
    {
        private static readonly ILogger Logger = LogManager.GetLogger(typeof(Worker).FullName);

        private readonly int _jobPollIntervalMilliseconds;

        public IServiceProvider Services { get; }

        public Worker(IServiceProvider services, IConfiguration configuration)
        {
            Services = services;
            _jobPollIntervalMilliseconds = configuration.GetValue<int>("JobPollIntervalMilliseconds");
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            Logger.Info("Worker running.");
            var task = new Task(o => DoWork(stoppingToken), stoppingToken);
            task.Start();
            return task;
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            Logger.Info("Worker stopping");
            await base.StopAsync(cancellationToken);
            Logger.Info("Worker stopped");
        }

        private void DoWork(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                using (var scope = Services.CreateScope())
                {
                    // do some work
                }

                Thread.Sleep(_jobPollIntervalMilliseconds);
            }
            Logger.Info("cancellation requested!");
        }        
    }
}

The problem
As I mentioned, we are setting this up as a daemon, like this

[Unit]
Description=my worker
Requires=deploy-my-worker.service
After=multi-user.target deploy-my-worker.service
ConditionFileIsExecutable=/home/my-worker/current/myworker
[Service]
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Environment=DOTNET_CLI_TELEMETRY_OPTOUT=true
Environment=ASPNETCORE_URLS=http://*:5000
Environment=DOTNET_ENVIRONMENT=Staging
Environment=ASPNETCORE_ENVIRONMENT=Staging
WorkingDirectory=/home/my-worker/current
ExecStart=/home/my-worker/current/myworker
SyslogIdentifier=my-worker
Restart=always
RestartSec=10
KillSignal=SIGTERM
User=usr
Group=usrgroup
[Install]
WantedBy=multi-user.target

The problem is that the worker will not stop gracefully. I am checking logs for the following log entries, but they do not appear:
Worker stopping, cancellation requested!, Worker stopped
Note that the application does shut down. What we have tried in order to shut down the service are the following:

  • shut down the server
  • systemctl stop my-worker.service
  • kill
  • kill -SIGTERM
  • kill -SIGINT

What Works

If I start the worker like this: usr@ip-10-168-19-126:~/my-worker/current$ ./myworker and then press Ctrl-C (which should be a SIGINT), the application stops, and in my logs I can see the correct messages:

2020-05-21 16:16:57.9676|INFO|MyApp.WorkerService.Worker|Worker stopping 
2020-05-21 16:16:57.9937|INFO|MyApp.WorkerService.Worker|cancellation requested! 
2020-05-21 16:16:57.9937|INFO|MyApp.WorkerService.Worker|Worker stopped 
2020-05-21 16:16:57.9980 Info AppDomain Shutting down. Logger closing...

Any ideas how I can get the daemon to work as expected?

NOTE:
I have good reason to believe that the problem lies somewhere in the daemon setup, or UseSystemd().
I replaced UseSystemd() with UseWindowsService() and installed it as a Windows service on a windows machine. Then went forward with starting and stopping the service via the Services panel, and saw shutdown logging as expected.
So, I am tempted to assume that there is no problem in the implementation, but rather somewhere in the setup.

解决方案

It seems that the problem lay with NLog's shutdown. This was fixed by doing the following: LogManager.AutoShutdown = false; and in Worker::StopAsync adding LogManager.Shutdown();

这篇关于.NET 核心 BackgroundService 不会作为守护程序正常关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆