加入收藏 | 设为首页 | 会员中心 | 我要投稿 北几岛 (https://www.beijidao.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

ASP.NET Core 3.x控制IHostedService启动顺序浅探

发布时间:2021-05-21 07:54:20 所属栏目:大数据 来源: https://www.jb51.cc
导读:想写好中间件,这是基

微信公众号:老王Plus

扫描二维码,关注个人公众号,可以第一时间得到最新的个人文章和内容推送

本文版权归作者所有,转载请保留此声明和原文链接

(编辑:北几岛)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

想写好中间件,这是基础。

?

一、前言

今天这个内容,基于于ASP.NET Core 3.x。

从3.x开始,ASP.NET Core使用了通用主机模式。它将WebHostBuilder放到了通用的IHost之上,这样可以确保Kestrel可以运行在IHostedService中。

我们今天就来研究一下这个启动方式和启动顺序。

????为了防止不提供原网址的转载,特在这里加上原文链接:https://www.cnblogs.com/tiger-wang/p/13636641.html

二、通常的启动次序

通常情况下,IHostedService的任何实现在添加到Startup.ConfigureServices()后,都会在GenericWebHostService之前启动。

这是微软官方给出的图。

这个图展示了在IHost上调用RunAsync()时的启动顺序(后者又调用StartAsync())。对我们来说,最重要的部分是启动的IHostedServices。从图上也可以看到,自定义IHostedServices先于GenericWebHostSevice启动。

我们来看一个简单的例子:

public?class?StartupHostedService?:?IHostedService
{
????private?readonly?ILogger?_logger;
????public?StartupHostedService(ILogger<StartupHostedService>?logger)
????
{
????????_logger?=?logger;
????}
????public?Task?StartAsync(CancellationToken?cancellationToken)
????
{
????????_logger.LogInformation("Starting?IHostedService?registered?in?Startup");
????????return?Task.CompletedTask;
????}
????StopAsync(CancellationToken?cancellationToken)
????
{
????????_logger.LogInformation("Stopping?IHostedService?registered?in?Startup");
????????return?Task.CompletedTask;
????}
}

我们做一个简单的IHostedService。希望加到Startup.cs中:

Startup
{
????public?void?ConfigureServices(IServiceCollection?services)
????
{
????????services.AddHostedService<StartupHostedService>();
????}
}

运行代码:

info:?demo.StartupHostedService[0]????????????#?这是上边的StartupHostedService
??????Starting?IHostedService?registered?in?Startup
info:?Microsoft.Hosting.Lifetime[#?这是GenericWebHostSevice
??????Now?listening?on:?https://localhost:5001
info:?Microsoft.Hosting.Lifetime[0]
??????Application?started.?Press?Ctrl+C?to?shut?down.

正如预期的那样,IHostedService首先执行,然后是GenericWebHostSeviceApplicationLifetime事件在所有IHostedServices执行之后触发。无论在什么地方注册了Startup.ConfigureServices()中的IHostedServiceGenericWebHostSevice都在最后启动。

?

那么问题来了,为什么GenericWebHostSevice在最后启动?

三、为什么`GenericWebHostSevice`在最后启动?

先看看多个IHostedService的情况。

当有多个IHostedService的实现加入到Startup.ConfigureServices()时,运行次序取决于它被加入的次序。

看例子:

Service1?:?IHostedService
{
????Service1(ILogger<Service1>?logger)
????{
????????_logger?=?logger;
????}
????"Starting?Service1");
????????"Stoping?Service1");
????????return?Task.CompletedTask;
????}
}
Service2?:
?IHostedService
{
????Service2(ILogger<Service2>?logger)
????{
????????_logger?=?logger;
????}
????"Starting?Service2");
????????"Stoping?Service2");
????????return?Task.CompletedTask;
????}
}

Startup.cs:

ConfigureServices(IServiceCollection?services)
????{
????????services.AddHostedService<Service1>();
????????services.AddHostedService<Service2>();
????}
}

运行:

info:?demo.Service1[0]????????????????#?这是Service1
??????Starting?Service1
info:?demo.Service2[#?这是Service2
??????Starting?Service2
info:?Microsoft.Hosting.Lifetime[0]????????0]
??????Application?started.?Press?Ctrl+C?to?shut?down.

?

那么,GenericWebHostSevice是什么时候注册的?

我们看看另一个文件Program.cs

Program
{
????static?Main(string[]?args)
????
{
????????CreateHostBuilder(args).Build().Run();
????}
????static?IHostBuilder?CreateHostBuilder(string[]?args)?=>
????????Host.CreateDefaultBuilder(args)
????????????.ConfigureWebHostDefaults(webBuilder?=>????????????#?这是GenericWebHostSevice注册的位置
????????????{
????????????????webBuilder.UseStartup<Startup>();
????????????});
}

ConfigureWebHostDefaults扩展方法调用ConfigureWebHost方法,该方法执行Startup.ConfigureServices(),然后注册GenericWebHostService。整理一下代码,就是下面这个样子:

ConfigureWebHost(this?IHostBuilder?builder,?Action<IWebHostBuilder>?configure)
{
????var?webhostBuilder?=?new?GenericWebHostBuilder(builder);

????configure(webhostBuilder);

????builder.ConfigureServices((context,?services)?=>?services.AddHostedService<GenericWebHostService>());
????return?builder;
}

这样可以确保GenericWebHostService总是最后运行,以保持通用主机实现和WebHost(已弃用)实现之间的行为一致。

?

因此,可以采用同样的方式,让IHostedServiceGenericWebHostService后面启动。

四、让`IHostedService`在`GenericWebHostService`后面启动

在大多数情况下,在GenericWebHostService之前启动IHostedServices就可以满足常规的应用。但是,GenericWebHostService还负责构建应用程序的中间件管道。如果IHostedService依赖于中间件管道或路由,那么就需要将它的启动延迟到GenericWebHostService完成之后。

根据上面的说明,在GenericWebHostService之后执行IHostedService的唯一方法是将它添加到GenericWebHostService之后的DI容器中。这意味着你必须跳出Startup.ConfigureServices(),在调用ConfigureWebHostDefaults之后,直接在IHostBuilder上调用ConfigureServices()

ProgramHostedService?:?IHostedService
{
????ProgramHostedService(ILogger<ProgramHostedService>?logger)
????{
????????_logger?=?logger;
????}
????"Starting?ProgramHostedService?registered?in?Program");
????????"Stopping?ProgramHostedService?registered?in?Program");
????????return?Task.CompletedTask;
????}
}

加到Program.cs中:

string[]?args)
????{
????????CreateHostBuilder(args).Build().Run();
????}

????string[]?args)?=>
????????Host.CreateDefaultBuilder(args)
????????????.ConfigureWebHostDefaults(webBuilder?=>????????????#?这是GenericWebHostSevice注册的位置
????????????{
????????????????webBuilder.UseStartup<Startup>();
????????????})
????????????.ConfigureServices(services?=>?
????????????????services.AddHostedService<ProgramHostedService>());????????????#?这是ProgramHostedService注册的位置
}

看输出:

#?这是StartupHostedService
??????Starting?IHostedService?registered?//localhost:5001
info:?demo.ProgramHostedService[#?这是ProgramHostedService
??????Starting?ProgramHostedService?registered?in?Program
info:?Microsoft.Hosting.Lifetime[0]
??????Application?started.?Press?Ctrl+C?to?shut?down.

同样,在关闭应用时,IHostedServices被反向停止,所以ProgramHostedService首先停止,接着是GenericWebHostSevice,最后是StartupHostedService

info:?Microsoft.Hosting.Lifetime[0]
??????Application?is?shutting?down...
demo.ProgramHostedServiceStopping?ProgramHostedService?registered?in?Program
.StartupHostedServiceIHostedService?Startup

五、总结

最后总结一下:

IHostedServices的执行顺序与它们在Startup.configureservices()中添加到DI容器中的顺序相同。运行侦听HTTP请求的Kestrel服务器的GenericWebHostSevice总是注册的IHostedServices之后运行。

要在GenericWebHostSevice之后启动IHostedService,需要在Program.cs中的IHostBuilder上ConfigureServices()扩展方法中进行注册。

(全文完)

本文的代码在:https://github.com/humornif/Demo-Code/tree/master/0024/demo

?


?

    推荐文章
      热点阅读