。NET核心BackgroundService不能作为守护程序正常关闭

问题描述 投票:0回答:1

我正在研究.NET Core 3.1后台服务,该服务将作为守护程序安装在Debian AWS EC2实例上。正常关闭守护程序以停止运行的任务并最终确定要处理的许多任务(发送一些事件等)非常重要。基本实现如下所示:

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>();
                });        
    }
}

您可以在这里看到我正在使用SystemdLifetime

工人如下:

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!");
        }        
    }
}

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

[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

问题是工人不会优雅地停下来。我正在检查以下日志条目的日志,但它们没有出现:Worker stoppingcancellation requested!Worker stopped请注意,应用程序确实关闭。为了关闭服务,我们尝试了以下操作:

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

什么有效

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

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...

关于如何使守护程序按预期运行的任何想法?

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

.net-core systemd background-service
1个回答
0
投票
您可以尝试一下:

protected override Task ExecuteAsync(CancellationToken stoppingToken) { System.Threading.Thread.CurrentThread.IsBackground = false; Logger.Info("Worker running."); var task = Task.Run(() => DoWork(stoppingToken), stoppingToken); task.Wait(); return task; }

关键是使您的线程正在执行任务,等待任务而不是在后台。我相信应用程序将在完成对ExecuteAsync的任何调用后退出。

© www.soinside.com 2019 - 2024. All rights reserved.