.NET 8 的 IHostedLifecycleService 接口是雞肋功能嗎?
.NET 8 引入了一個新的接口,叫做IHostedLifecycleService,這個接口繼承自現有的 IHostedService 接口,它為 BackgroundService 提供了一些新的生命周期事件的方法:
- StartingAsync:在 StartAsync 方法之前執行,用于執行一些初始化或預處理的邏輯。
- StartedAsync:在 StartAsync 方法之后執行,用于執行一些后處理或檢查的邏輯。
- StoppingAsync:在 StopAsync 方法之前執行,用于執行一些清理或釋放的邏輯。
- StoppedAsync:在 StopAsync 方法之后執行,用于執行一些收尾或報告的邏輯。
這些方法都發生在現有的 StartAsync 和 StopAsync 方法之前或之后。
示例代碼
下面的示例演示如何使用新 API:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<MyIOWorker>();
var host = builder.Build();
host.Run();
public class MyIOWorker : BackgroundService, IHostedLifecycleService
{
public async Task StartingAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Starting");//業務邏輯
}
public async Task StartedAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Started");//業務邏輯
}
public async Task StoppingAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Stopping");//業務邏輯
}
public async Task StoppedAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Stopped");//業務邏輯
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine($"{nameof(MyIOWorker)} Execute");//業務邏輯
await Task.Delay(1000, stoppingToken);
}
}
}
輸出結果如下:
MyIOService Starting
MyIOService Execute
MyIOService Started
...
MyIOService Stopping
MyIOService Stopped
雞肋功能?
但是,直接使用 IHostedService 接口一樣可以實現相同功能:
public class MyIOWorker : BackgroundService
{
public override async Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Starting");//業務邏輯
await base.StartAsync(cancellationToken);
Console.WriteLine($"{nameof(MyIOWorker)} Started");//業務邏輯
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"{nameof(MyIOWorker)} Stopping");//業務邏輯
await base.StopAsync(cancellationToken);
Console.WriteLine($"{nameof(MyIOWorker)} Stopped");//業務邏輯
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine($"{nameof(MyIOWorker)} ExecuteAsync");//業務邏輯
await Task.Delay(1000, stoppingToken);
}
}
}
那么,新特性IHostedLifecycleService的意義何在呢?
僅僅為了,方便放置不同邏輯的代碼嗎?
探究源碼
在dotnet/runtime源碼https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs中,我們找到了 IHostedLifecycleService 的使用邏輯:
// Call StartingAsync().
if (_hostedLifecycleServices is not null)
{
await ForeachService(_hostedLifecycleServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
(service, token) => service.StartingAsync(token)).ConfigureAwait(false);
// Exceptions in StartingAsync cause startup to be aborted.
LogAndRethrow();
}
// Call StartAsync().
await ForeachService(_hostedServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
async (service, token) =>
{
await service.StartAsync(token).ConfigureAwait(false);
if (service is BackgroundService backgroundService)
{
_ = TryExecuteBackgroundServiceAsync(backgroundService);
}
}).ConfigureAwait(false);
// Exceptions in StartAsync cause startup to be aborted.
LogAndRethrow();
// Call StartedAsync().
if (_hostedLifecycleServices is not null)
{
await ForeachService(_hostedLifecycleServices, cancellationToken, concurrent, abortOnFirstException, exceptions,
(service, token) => service.StartedAsync(token)).ConfigureAwait(false);
}
上面的代碼先遍歷執行IEnumerable<IHostedLifecycleService>? _hostedLifecycleServices的StartingAsync方法,再遍歷執行IEnumerable<IHostedService>? _hostedServices的StartAsync方法。
也就是說,如果存在多個IHostedLifecycleService實現,我們可以把初始化代碼放在StartingAsync方法實現中,保證了全部初始化邏輯執行成功后才會執行StartAsync方法中正式的業務邏輯。對于StopAsync方法也是同理。
使用場景
比如,如果直接使用 IHostedService 接口:
builder.Services.AddHostedService<AWorker>();
builder.Services.AddHostedService<BWorker>();
public class AWorker : BackgroundService
{
public override async Task StartAsync(CancellationToken cancellationToken)
{
//初始化數據庫A表
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//訪問數據庫A表和B表
}
}
public class BWorker : BackgroundService
{
public override async Task StartAsync(CancellationToken cancellationToken)
{
//初始化數據庫B表
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//訪問數據庫A表和B表
}
}
由于執行有先后順序,初始化數據庫B表操作還沒有執行,AWorker 就已經開始執行ExecuteAsync方法了,AWorker 的訪問數據庫A表和B表操作可能產生不可預料的結果。
現在使用IHostedLifecycleService,將初始化放在生命周期的早期:
public class AWorker : BackgroundService, IHostedLifecycleService
{
public async Task StartingAsync(CancellationToken cancellationToken)
{
//初始化數據庫A表
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//訪問數據庫A表和B表
}
}
public class BWorker : BackgroundService, IHostedLifecycleService
{
public async Task StartingAsync(CancellationToken cancellationToken)
{
//初始化數據庫B表
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//訪問數據庫A表和B表
}
}
現在,訪問數據庫A表和B表操作可以保證正常執行了。
默認情況下,多個IHostedLifecycleService實現是按順序執行的,我們還可以設置它們并發啟動和停止,節約整體啟動時間:
builder.Services.Configure<HostOptions>(options =>
{
options.ServicesStartConcurrently = true;
options.ServicesStopConcurrently = true;
});
總結
IHostedLifecycleService是.NET 8中引入的一個新特性,它可以讓我們在使用多個IHostedService實現的時候,更加靈活和高效地控制它們的啟動和停止,避免出現不必要的依賴和沖突。