如何使用.NET 6的IHostedService和BackgroundService?

大家好,我是張飛洪,感謝您的閱讀,我會不定期和你分享學習心得,希望我的文章能成爲你成長路上的墊腳石,讓我們一起精進。

本章是《定製ASP NET 6.0框架系列文章》的第七篇。本文內容和定製無關,主要是關於創建後臺服務,用於異步運行任務的功能,我們會使用此功能定期從遠程服務獲取數據。
本文的主題主要包括:

  • IHostedService介紹
  • BackgroundService介紹
  • Worker Service介紹

1 技術要求

爲了演示,我們先創建一個ASP.NET Core應用,我們使用控制檯(ShellBash)終端,切換到工作目錄,執行以下命令,創建一個MVC應用程序:

dotnet new mvc -n HostedServiceSample -o HostedServiceSample

使用Visual Studio雙擊打開項目文件,你也可以使用VS Code打開項目,並在已打開的控制檯中執行以下命令:

cd HostedServiceSample code .

2 IHostedService介紹

託管服務自ASP.NET Core 2.0開始出現,可在應用後臺異步運行任務。比如,可以定期獲取數據、在後臺進行一些計算或進行一些數據清理。甚至,我們還可以使它發送預配置的電子郵件,或者在後臺執行任意的邏輯操作。
託管服務必須實現IHostedService接口,如下代碼所示:

public class SampleHostedService : IHostedService {     
    public Task StartAsync(CancellationToken cancellationToken)     {     }     
    public Task StopAsync(CancellationToken cancellationToken)     {     } 
}

IHostedService需要實現StartAsync()StopAsync()方法。StartAsync()是實現要執行的邏輯的地方,該方法在應用程序啓動後立即執行,並且只執行一次;StopAsync()方法在應用程序停止之前執行。這意味着要我們需要自己實現一個計劃服務,實現一個定期執行代碼的循環。
要執行IHostedService,還需要在ASP.NET Core依賴注入容器註冊作爲單例實例:

builder.Services.AddSingleton<IHostedService, SampleHostedService>();

我們再看下面這個示例,用於展示託管服務的工作方式。它會在啓動、停止和每2秒向控制檯寫入一條日誌消息:
首先,我們編寫類的骨架,並通過DI反轉一個ILogger的實例:

namespace HostedServiceSample; 
public class SampleHostedService:IHostedService {     
    private readonly ILogger<SampleHostedService> logger;        
    public  SampleHostedService(ILogger<SampleHostedService> logger) {                                 
        this.logger = logger;
    }     
    public Task StartAsync(CancellationToken cancellationToken)     
    {}     
    public Task StopAsync(CancellationToken cancellationToken)     
    {} 
}

下一步,實現StopAsync方法。此方法用於需要關閉連接時需要處理的邏輯工作:

public Task StopAsync(CancellationToken cancellationToken) 
{     
    logger.LogInformation("託管服務停止……");     
    return Task.CompletedTask;
}

實際工作將在StartAsync方法內完成:

public Task StartAsync(CancellationToken cancellationToken) {         
    logger.LogInformation("託管服務開始……");     
    return Task.Factory.StartNew(async () => {             
        while (!cancellationToken.IsCancellationRequested) {
            logger.LogInformation($"託管服務執行中 - {DateTime.Now}");             
            try{                 
                //等待2秒                
                await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); 
            }catch (OperationCanceledException) { } 
        }
     }, cancellationToken); 
}

接下來運行並測試一下,效果如下圖所示,日誌每2秒寫入一次控制檯。:

dotnet run

3 BackgroundService介紹

BackgroundService類是在ASP.NET Core 3.0中被引入的,它是一個實現IHostedService接口的抽象類,它還提供了一個名爲ExecuteAsync的抽象方法,該方法返回一個Task任務。
我們重寫一下上面示例的託管服務,先搭建一個骨架類,如下所示:

namespace HostedServiceSample; 
public class SampleBackgroundService : BackgroundService {     
    private readonly ILogger<SampleHostedService> logger;     
    public SampleBackgroundService(ILogger<SampleHostedService> logger){
        this.logger = logger;     
    }
}

接下來重寫StopAsync方法:

public override async Task StopAsync(CancellationToken cancellationToken) {
    logger.LogInformation("後臺服務停止……");     
    await Task.CompletedTask; 
}

最後,我們重寫ExecuteAsync方法:

protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
    logger.LogInformation("後臺服務啓動……");     
    await Task.Factory.StartNew(async () =>{         
        while(!cancellationToken.IsCancellationRequested) {
            logger.LogInformation($"後臺服務執行中 - {DateTime.Now}");
            try{                 
                await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);             
            }catch (OperationCanceledException) {}         
        }     
    }, cancellationToken); 
}

註冊也要重寫一下,在ASP.NET Core 3.0及更高版本中,ServiceCollection有一個新的擴展方法來註冊託管服務或後臺工作程序:

builder.Services.AddHostedService<SampleBackgroundService>();

在控制檯中調用以下命令啓動應用程序測試:

dotnet run

測試結果和上頭一樣,這裏不再累述。

4 Worker Service介紹

Worker Service可以叫執行者或者工作者。
ASP.NET Core 3.0及更高版本中,創建簡單的工作者服務變得非常容易,這個服務可以也託管在非Web服務器裏。
我們再新建一個項目:

dotnet new worker -n BackgroundServiceSample -o BackgroundServiceSample

我們可以看到,該命令會創建一個帶有Program.csWorker.cs的控制檯應用程序。Worker.cs包含Worker類,它是從BackgroundService類繼承的。在ASP.NET 5.0及更早版本,Program.cs文件看起來與以前版本的很相似,但沒WebHostBuilder

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

ASP.NET Core 6.0中,Program.cs以與迷你API相同,都被簡化掉了。它看起來像這樣:

using BackgroundServiceSample; 
IHost host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{
    services.AddHostedService<Worker>();
}).Build(); 
await host.RunAsync();

這裏創建IHost並啓用依賴注入,我們可以在任何類型的.NET Core應用程序中使用依賴注入,而不僅僅是在ASP.NET Core應用程序。
然後,我們將工作進程添加到服務集合中,這樣就可以將服務作爲Windows服務或Docker容器中的後臺程序運行。

小結

有了上面的拋磚引玉,現在您可以使用IHostedServiceBackgroundService開始做一些更復雜的事情了。
如果後臺服務和應用都在同一個進程中運行,會消耗太多的CPU或內存,甚至降低應用的速度。
所以,對於大型的應用程序,建議單獨創建一個用於執行後臺任務的應用程序中,可以部署在單獨的Docker容器裏、或者雲服務器裏。一定要確保它與主應用程序分開。
在下一章中,我們將進入中間件的學習,以及如何使用中間件在請求管道上實現特殊邏輯,感謝您的閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章