.NET 8 引入了一个新的接口,叫做IHostedLifecycleService,这个接口继承自现有的 IHostedService 接口,它为 BackgroundService 提供了一些新的生命周期事件的方法:
这些方法都发生在现有的 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 StartingMyIOService ExecuteMyIOService Started...MyIOService StoppingMyIOService 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实现的时候,更加灵活和高效地控制它们的启动和停止,避免出现不必要的依赖和冲突。
本文链接:http://www.28at.com/showinfo-26-56563-0.html.NET 8 的 IHostedLifecycleService 接口是鸡肋功能吗?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: Pulsar3.0新功能,你了解了吗?
下一篇: 如何提高 Java 代码的可重用性