.NET Worker Service 怎样雅致撤出

上一篇文章中大家了解了 .NET Worker Service 的新手入门专业知识[1],今日大家然后介绍一下怎样雅致地关掉和撤出 Worker Service。

Worker 类

从上一篇文章中,大家早已知道 Worker Service 模版为大家给予三个拆箱既用的关键文档,在其中 Worker 类是承继自抽象性基类 BackgroundService 的,而 BackgroundService 完成了 IHostedService 插口。最后 Worker 类会被申请注册为托管服务,大家解决每日任务的关键编码便是写在 Worker 类中的。因此 ,大家必须关键了解一下 Worker 以及基类。

先讨论一下它的基类 BackgroundService

Background Service

基类 BackgroundService 中有三个可调用的方式 ,能够 使我们关联到应用软件的生命期中:

  • 抽象方法 ExecuteAsync:做为应用软件关键通道点的方式 。假如此方式 撤出,则应用软件将关掉。大家务必在 Worker 中完成它。
  • 虚方式 StartAsync:在应用软件运作时启用。假如必须,能够 调用此方式 ,它可用以在服务项目运作时一次性地设定資源;自然,还可以忽视它。
  • 虚方式 StopAsync:在应用软件关掉时启用。假如必须,能够 调用此方式 ,在关掉时释放出来資源和消毁目标;自然,还可以忽视它。

默认设置状况下 Worker 只调用必需的抽象方法 ExecuteAsync

新创建一个 Worker Service 新项目

大家来新创建一个 Worker Service,应用 Task.Delay 来仿真模拟关掉前务必进行的一些实际操作,看一下是不是能够 根据简易地在 ExecuteAsyncDelay 来仿真模拟完成雅致关掉。

必须采用的开发环境:

  • Visual Studio Code:https://code.visualstudio.com/
  • 全新的 .NET SDK:https://dotnet.microsoft.com/download

安裝好之上专用工具后,在终端设备中运作下列指令,建立一个 Worker Service 新项目:

dotnet new Worker -n "MyService"

建立好 Worker Service 后,在 Visual Studio Code 中开启应用软件,随后搭建并运作一下,以保证一切正常:

dotnet build
dotnet run

CTRL C 键关掉服务项目,服务项目会马上撤出,默认设置状况下 Worker Service 的关掉便是那么立即!在许多情景(例如运行内存中的序列)中,这不是大家要想的結果,有时候大家迫不得已在服务项目关掉前进行一些必需的废物回收或事务管理

大家看一下 Worker 类的编码,会见到它只调用了基类 BackgroundService 中的抽象方法 ExecuteAsync

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
        await Task.Delay(1000, stoppingToken);
    }
}

大家试着改动一下此方式 ,撤出前做一些业务流程解决:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
        // await Task.Delay(1000, stoppingToken);
        await Task.Delay(1000);
    }

    _logger.LogInformation("等候撤出 {time}", DateTimeOffset.Now);

    Task.Delay(60_000).Wait(); //仿真模拟撤出前必须进行的工作中

    _logger.LogInformation("撤出 {time}", DateTimeOffset.Now);
}

随后测试一下,看它是否会像大家预估的那般先等候 60 秒再关掉。

dotnet build
dotnet run

CTRL C 键关掉服务项目,大家会发觉,它在輸出 “等候撤出” 后,并沒有等候 60 秒并輸出 “撤出” 以后再关掉,只是迅速便撤出了。这如同大家了解的控制面板应用软件,默认设置状况下,在大家点了右上方的关闭按钮或是按住 CTRL C 键时,会立即关掉一样。

Worker Service 雅致撤出

那麼,怎样才能完成雅致撤出呢?

方式 其实不是很难,那便是将 IHostApplicationLifetime 引入到大家的服务项目中,随后在应用软件终止时手动式启用 IHostApplicationLifetimeStopApplication 方式 来关掉应用软件。

改动 Worker 的构造方法,引入 IHostApplicationLifetime

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

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

随后在 ExecuteAsync 中,解决完撤出前务必进行的业务流程逻辑性后,手动式启用 IHostApplicationLifetimeStopApplication 方式 ,下边是丰富多彩过的 ExecuteAsync 编码:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    try
    {
        // 这儿完成具体的领域模型
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                await SomeMethodThatDoesTheWork(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Global exception occurred. Will resume in a moment.");
            }

            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
    }
    finally
    {
        _logger.LogWarning("Exiting application...");
        GetOffWork(stoppingToken); //关掉前必须进行的工作中
        _hostApplicationLifetime.StopApplication(); //手动式启用 StopApplication
    }
}

private async Task SomeMethodThatDoesTheWork(CancellationToken cancellationToken)
{
    _logger.LogInformation("我爱工作,艰苦奋斗ing……");
    await Task.CompletedTask;
}

/// <summary>
/// 关掉前必须进行的工作中
/// </summary>
private void GetOffWork(CancellationToken cancellationToken)
{
    _logger.LogInformation("啊,槽糕,有一个应急 bug 必须下班了前进行!!!");

    _logger.LogInformation("啊啊啊啊啊啊,我喜欢加班加点,我想再干 20 秒,Wait 1 ");

    Task.Delay(TimeSpan.FromSeconds(20)).Wait();

    _logger.LogInformation("啊啊啊啊啊,我喜欢加班加点,我想再干 1 分鐘,Wait 2 ");

    Task.Delay(TimeSpan.FromMinutes(1)).Wait();

    _logger.LogInformation("啊啊哈哈哈,总算好啦,下班了离开!");
}

这时,再度 dotnet run 运作服务项目,随后按 CTRL C 键关掉服务项目,您会发觉关掉前必须进行的工作中 GetOffWork 运作进行后才会撤出服务项目了。

到此,大家早已完成了 Worker Service 的雅致撤出。

StartAsync 和 StopAsync

为了更好地更进一步掌握 Worker Service,大家再说丰富多彩一下大家的编码,调用基类 BackgroundServiceStartAsyncStopAsync 方式 :

public class Worker : BackgroundService
{
    private bool _isStopping = false; //是不是已经停止工作
    private readonly IHostApplicationLifetime _hostApplicationLifetime;
    private readonly ILogger<Worker> _logger;

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

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("上班了,也是精神焕发的一天,output from StartAsync");
        return base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            // 这儿完成具体的领域模型
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                    await SomeMethodThatDoesTheWork(stoppingToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Global exception occurred. Will resume in a moment.");
                }

                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }
        }
        finally
        {
            _logger.LogWarning("Exiting application...");
            GetOffWork(stoppingToken); //关掉前必须进行的工作中
            _hostApplicationLifetime.StopApplication(); //手动式启用 StopApplication
        }
    }

    private async Task SomeMethodThatDoesTheWork(CancellationToken cancellationToken)
    {
        if (_isStopping)
            _logger.LogInformation("装作仍在艰苦奋斗ing…… 实际上我要去洗水杯了");
        else
            _logger.LogInformation("我爱工作,艰苦奋斗ing……");

        await Task.CompletedTask;
    }

    /// <summary>
    /// 关掉前必须进行的工作中
    /// </summary>
    private void GetOffWork(CancellationToken cancellationToken)
    {
        _logger.LogInformation("啊,槽糕,有一个应急 bug 必须下班了前进行!!!");

        _logger.LogInformation("啊啊啊啊啊啊,我喜欢加班加点,我想再干 20 秒,Wait 1 ");

        Task.Delay(TimeSpan.FromSeconds(20)).Wait();

        _logger.LogInformation("啊啊啊啊啊,我喜欢加班加点,我想再干 1 分鐘,Wait 2 ");

        Task.Delay(TimeSpan.FromMinutes(1)).Wait();

        _logger.LogInformation("啊啊哈哈哈,总算好啦,下班了离开!");
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("太棒了,休息时间到,output from StopAsync at: {time}", DateTimeOffset.Now);

        _isStopping = true;

        _logger.LogInformation("去洗一洗杯子先……", DateTimeOffset.Now);
        Task.Delay(30_000).Wait();
        _logger.LogInformation("杯子洗好啦。", DateTimeOffset.Now);

        _logger.LogInformation("下班了喽 ^_^", DateTimeOffset.Now);

        return base.StopAsync(cancellationToken);
    }
}

再次运作一下

dotnet build
dotnet run

随后按 CTRL C 键关掉服务项目,看一下运作結果是啥?

我们可以观查到在 Worker Service 运行和关掉时,基类 BackgroundService 中可调用的三个方式 的运作次序各自如下图所显示:

worker service startup flowchart

worker service shutdown flowchart

汇总

在文中中,我根据一个案例详细介绍了怎样雅致撤出 Worker Service 的有关专业知识。

Worker Service 实质上仍是一个控制面板应用软件,实行一个工作。但它不但能够 做为控制面板应用软件立即运作,还可以应用 sc.exe 常用工具安裝为 Windows 服务项目,还能够布署到 linux 设备上做为后台进程运作。之后有时间我能详细介绍大量有关 Worker Service 的专业知识。

您能够 从 GitHub 免费下载文中中的源代码[2]


创作者 : 技术性译民
荣誉出品 : 技术性译站


  1. https://mp.weixin.qq.com/s/ujGkb5oaXq3lqX_g_eQ3_g .NET Worker Service 新手入门详细介绍 ↩︎

  2. https://github.com/ITTranslate/WorkerServiceGracefullyShutdown 网站源码下载 ↩︎

评论(0条)

刀客源码 游客评论