不論其是否遵循微服務架構模式,背景工作和排程工作都是您可能需要在任何應用程式中使用的專案。 使用微服務架構時的差異在於,您可以在不同的進程/容器中實作背景工作以進行裝載,以便根據您的需求縮減或擴展。
從一般的觀點來看,在 .NET 中,我們稱之為這些類型的「 託管服務」,因為它們是您裝載於主機/應用程式/微服務內的服務/邏輯。 請注意,在此情況下,託管服務只是表示具有背景工作邏輯的類別。
由於 .NET Core 2.0,架構會提供名為 IHostedService 的新介面,協助您輕鬆地實作託管服務。 基本概念是,您可以註冊多個在背景中執行的背景工作(託管服務),這些工作會在您的網頁主機或伺服器運行時執行,如圖 6-26 所示。
圖 6-26。 在 WebHost 與 Host 中使用 IHostedService
ASP.NET Core 1.x 和 2.x 支援在 web 應用程式中執行背景處理程序IWebHost
。 .NET Core 2.1 和更新版本支援在一般控制台應用程式上執行背景進程的 IHost
。 請注意WebHost
和Host
之間的差異。
在 ASP.NET Core 2.0 中,WebHost
是一個實作 IWebHost
的基類,您可以將其作為基礎結構成品,用來為您的程序提供 HTTP 伺服器功能,例如在您建立 MVC 網頁應用程式或 Web API 服務時。 它提供 ASP.NET Core 中所有新的基礎結構特性,使您可以使用相依性注入、在請求管線中插入中間件等。
WebHost
使用這些相同的 IHostedServices
來進行背景工作。
在 .NET Core 2.1 中引進了 一個 Host
(基類實作 IHost
)。 基本上, Host
可讓您擁有與您擁有 WebHost
的類似基礎結構(相依性插入、託管服務等),但在此情況下,您只想擁有簡單且較輕的程式作為主機,與 MVC、Web API 或 HTTP 伺服器功能無關。
因此,您可以選擇並建立專用的主機處理程式 IHost
來處理託管服務,而不需要其他任何專案,這類微服務只是為了裝載 IHostedServices
,或者您也可以擴充現有的 ASP.NET Core WebHost
,例如現有的 ASP.NET Core Web API 或 MVC 應用程式。
每個方法都有優缺點,視您的商務和延展性需求而定。 底線基本上是,如果您的背景工作與 HTTP (IWebHost
) 無關,您應該使用 IHost
。
在 WebHost 或主機中註冊託管服務
讓我們進一步深入探討IHostedService
介面,因為它的使用方式不論是在WebHost
還是Host
中都相當類似。
SignalR 是使用託管服務成品的其中一個範例,但您也可以將其用於更簡單的專案,例如:
- 背景工作輪詢資料庫以尋找變更。
- 週期性更新某些快取的排程任務。
- QueueBackgroundWorkItem 的實作,可讓工作在背景線程上執行。
- 在 Web 應用程式的背景中處理來自消息佇列的訊息,同時共用像
ILogger
這樣的一般服務。 - 使用
Task.Run()
啟動了一項背景任務。
您基本上可以將任何這些動作委派至實作 IHostedService
的背景工作。
您在 IHostedServices
或 WebHost
中新增一或多個 Host
的方式,是透過 ASP.NET Core AddHostedService 中的 WebHost
擴充方法註冊它們(或在 .NET Core 2.1 和更新版本中的 Host
中)。 基本上,您必須在 Program.cs 中註冊應用程式啟動內的託管服務。
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
在該程式代碼中,GracePeriodManagerService
託管服務是 eShopOnContainers 中 Ordering 商務微服務的實際程式代碼,而其餘兩部分則僅僅是附加範例。
背景 IHostedService
任務執行與應用程式的存留期協調,無論是主機還是微服務。 您可以在應用程式啟動時註冊工作,並在應用程式關閉時有機會執行一些優雅的措施或進行清理。
如果沒有使用 IHostedService
,您一律可以啟動背景線程來執行任何工作。 差異正是在應用程式的關機時間,該線程只會被終止,而不需要有機會執行正常清除動作。
IHostedService 介面
當您註冊 IHostedService
時,.NET 會在應用程式啟動和停止期間分別呼叫 StartAsync()
類型的 StopAsync()
和 IHostedService
方法。 如需詳細資訊,請參閱 IHostedService 介面。
如您所想像,您可以建立多個 IHostedService 實作,並在 Program.cs中註冊每個實作,如先前所示。 所有裝載的服務都會隨著應用程式/微服務一起啟動和停止。
當主機觸發StopAsync()
方法時,身為開發人員的您必須負責處理服務的停止動作。
使用衍生自 BackgroundService 基類的自定義託管服務類別實作 IHostedService
您可以立即著手從頭建立自訂的託管服務類別,並實作 IHostedService
,因為使用 .NET Core 2.0 和更新的版本時需要這麼做。
不過,由於大部分的背景工作任務對於取消標記的管理和其他一般作業會有類似的需求,因此您可以衍生自一個名為BackgroundService
的便利抽象基類(自 .NET Core 2.1 起可用)。
該類別提供設定背景工作所需的主要功能。
下一個程式代碼是 .NET 中實作的抽象 BackgroundService 基類。
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
當從之前的抽象基類衍生時,因為繼承的實作,您只需在自己的自定義託管服務類別中實作ExecuteAsync()
方法,如下列來自eShopOnContainers的簡化程序代碼,其在需要時會輪詢資料庫,並將整合事件發佈到事件總線。
public class GracePeriodManagerService : BackgroundService
{
private readonly ILogger<GracePeriodManagerService> _logger;
private readonly OrderingBackgroundSettings _settings;
private readonly IEventBus _eventBus;
public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
{
// Constructor's parameters validations...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"GracePeriodManagerService is starting.");
stoppingToken.Register(() =>
_logger.LogDebug($" GracePeriod background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"GracePeriod task doing background work.");
// This eShopOnContainers method is querying a database table
// and publishing events into the Event Bus (RabbitMQ / ServiceBus)
CheckConfirmedGracePeriodOrders();
try {
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
catch (TaskCanceledException exception) {
_logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
}
}
_logger.LogDebug($"GracePeriod background task is stopping.");
}
.../...
}
在此 eShopOnContainers 的特定案例中,它會執行應用程式方法,以查詢資料庫數據表,尋找具有特定狀態的訂單,並在套用變更時,它會透過事件總線發佈整合事件(其下方可以使用 RabbitMQ 或 Azure 服務總線)。
當然,您可以改為執行任何其他業務背景工作。
根據預設,取消令牌會設定為5秒超時,不過您可以在運用WebHost
的UseShutdownTimeout
延伸模組來構建IWebHostBuilder
時變更該值。 這表示我們的服務預計將在5秒內取消,否則會更突然終止。
下列程式代碼會將該時間變更為10秒。
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
摘要類別圖表
下圖顯示實作 IHostedServices 時所涉及類別和介面的可視化摘要。
圖 6-27。 顯示與 IHostedService 相關的多個類別和介面的類別圖表
類別圖表:IWebHost 和 IHost 可以裝載許多繼承自 BackgroundService 的服務,後者會實作 IHostedService。
部署考慮和重點
請務必注意,部署 ASP.NET Core WebHost
或 .NET Host
的方式可能會影響最終解決方案。 例如,如果您在 IIS 或一般 Azure App Service 上部署 WebHost
,您的主機可能會因為應用程式集區回收而關閉。 但是,如果您將主機以容器形式部署到 Kubernetes 協作器上,您可以控制主機的運行中的實例數目。 此外,您也可以考慮雲端中的其他方法,特別是針對這些案例所做的方法,例如 Azure Functions。 最後,如果您需要服務一直執行,而且部署在 Windows Server 上,您可以使用 Windows 服務。
但即使是部署至應用程式集區的 WebHost
,仍有一些情形,例如重新填入或排清應用程式的記憶體快取,這些做法仍然適用。
介面 IHostedService
提供方便的方式,在 ASP.NET Core Web 應用程式中啟動背景工作(在 .NET Core 2.0 和更新版本中),或任何進程/主機中(從 .NET Core 2.1 開始使用 IHost
)。 其主要優點是,當主機本身正在關閉時,您有機會順利取消,以清除背景工作的程序代碼。
其他資源
在 ASP.NET Core/Standard 2.0 中建置排程的工作
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.html在 ASP.NET Core 2.0 中實作 IHostedService
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice使用 ASP.NET Core 2.1 的 GenericHost 範例
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample