ASP.NET Core 3.x控制IHostedService启动顺序浅探
想写好中间件,这是基础。
一、前言
今天这个内容,基于于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;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping IHostedService registered in Startup");
return Task.CompletedTask;
}
}
我们做一个简单的IHostedService。希望加到Startup.cs中:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<StartupHostedService>();
}
}
运行代码:
info: demo.StartupHostedService[0] # 这是上边的StartupHostedService
Starting IHostedService registered in Startup
info: Microsoft.Hosting.Lifetime[0] # 这是GenericWebHostSevice
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
正如预期的那样,IHostedService首先执行,然后是GenericWebHostSevice。ApplicationLifetime事件在所有IHostedServices执行之后触发。无论在什么地方注册了Startup.ConfigureServices()中的IHostedService, GenericWebHostSevice都在最后启动。
那么问题来了,为什么GenericWebHostSevice在最后启动?
三、为什么`GenericWebHostSevice`在最后启动?
先看看多个IHostedService的情况。
当有多个IHostedService的实现加入到Startup.ConfigureServices()时,运行次序取决于它被加入的次序。
看例子:
public class Service1 : IHostedService
{
private readonly ILogger _logger;
public Service1(ILogger<Service1> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting Service1");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stoping Service1");
return Task.CompletedTask;
}
}
public class Service2 : IHostedService
{
private readonly ILogger _logger;
public Service2(ILogger<Service2> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting Service2");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stoping Service2");
return Task.CompletedTask;
}
}
Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<Service1>();
services.AddHostedService<Service2>();
}
}
运行:
info: demo.Service1[0] # 这是Service1
Starting Service1
info: demo.Service2[0] # 这是Service2
Starting Service2
info: Microsoft.Hosting.Lifetime[0] # 这是GenericWebHostSevice
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
那么,GenericWebHostSevice是什么时候注册的?
我们看看另一个文件Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => # 这是GenericWebHostSevice注册的位置
{
webBuilder.UseStartup<Startup>();
});
}
ConfigureWebHostDefaults扩展方法调用ConfigureWebHost方法,该方法执行Startup.ConfigureServices(),然后注册GenericWebHostService。整理一下代码,就是下面这个样子:
public static IHostBuilder 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(已弃用)实现之间的行为一致。
因此,可以采用同样的方式,让IHostedService在GenericWebHostService后面启动。
四、让`IHostedService`在`GenericWebHostService`后面启动
在大多数情况下,在GenericWebHostService之前启动IHostedServices就可以满足常规的应用。但是,GenericWebHostService还负责构建应用程序的中间件管道。如果IHostedService依赖于中间件管道或路由,那么就需要将它的启动延迟到GenericWebHostService完成之后。
根据上面的说明,在GenericWebHostService之后执行IHostedService的唯一方法是将它添加到GenericWebHostService之后的DI容器中。这意味着你必须跳出Startup.ConfigureServices(),在调用ConfigureWebHostDefaults之后,直接在IHostBuilder上调用ConfigureServices():
public class ProgramHostedService : IHostedService
{
private readonly ILogger _logger;
public ProgramHostedService(ILogger<ProgramHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting ProgramHostedService registered in Program");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping ProgramHostedService registered in Program");
return Task.CompletedTask;
}
}
加到Program.cs中:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => # 这是GenericWebHostSevice注册的位置
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServices(services =>
services.AddHostedService<ProgramHostedService>()); # 这是ProgramHostedService注册的位置
}
看输出:
info: demo.StartupHostedService[0] # 这是StartupHostedService
Starting IHostedService registered in Startup
info: Microsoft.Hosting.Lifetime[0] # 这是GenericWebHostSevice
Now listening on: https://localhost:5001
info: demo.ProgramHostedService[0] # 这是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...
info: demo.ProgramHostedService[0]
Stopping ProgramHostedService registered in Program
info: demo.StartupHostedService[0]
Stopping IHostedService registered in 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
![]() |
微信公众号:老王Plus 扫描二维码,关注个人公众号,可以第一时间得到最新的个人文章和内容推送 本文版权归作者所有,转载请保留此声明和原文链接 |
ASP.NET Core 3.x控制IHostedService启动顺序浅探的更多相关文章
- 探索ASP.Net Core 3.0系列六:ASP.NET Core 3.0新特性启动信息中的结构化日志
前言:在本文中,我将聊聊在ASP.NET Core 3.0中细小的变化——启动时记录消息的方式进行小的更改. 现在,ASP.NET Core不再将消息直接记录到控制台,而是正确使用了logging 基 ...
- 避免在ASP.NET Core 3.0中为启动类注入服务
本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...
- 【ASP.NET Core】运行原理之启动WebHost
ASP.NET Core运行原理之启动WebHost 本节将分析WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build ...
- ASP.NET Core基础1:应用启动流程
先看下ASP.NET Core的启动代码,如下图: 通过以上代码,我们可以初步得出以下结论: 所有的ASP.NET Core程序本质上也是一个控制台程序,使用Program的Main方法作为程序的入口 ...
- 【ASP.NET Core】运行原理(2):启动WebHost
本系列将分析ASP.NET Core运行原理 [ASP.NET Core]运行原理[1]:创建WebHost [ASP.NET Core]运行原理[2]:启动WebHost [ASP.NET Core ...
- asp.net core 使用 AccessControlHelper 控制访问权限
asp.net core 使用 AccessControlHelper 控制访问权限 Intro 由于项目需要,需要在基于 asp.net mvc 的 Web 项目框架中做权限的控制,于是才有了这个权 ...
- 在ASP.NET Core配置环境变量和启动设置
在这一部分内容中,我们来讨论ASP.NET Core中的一个新功能:环境变量和启动设置,它将开发过程中的调试和测试变的更加简单.我们只需要简单的修改配置文件,就可以实现开发.预演.生产环境的切换. A ...
- ASP.NET Core配置环境变量和启动设置
在这一部分内容中,我们来讨论ASP.NET Core中的一个新功能:环境变量和启动设置,它将开发过程中的调试和测试变的更加简单.我们只需要简单的修改配置文件,就可以实现开发.预演.生产环境的切换. A ...
- [转]ASP.NET Core配置环境变量和启动设置
本文转自:https://www.cnblogs.com/tdfblog/p/Environments-LaunchSettings-in-Asp-Net-Core.html 在这一部分内容中,我们来 ...
随机推荐
- “随手记”APP与已经发布的记账软件“鲨鱼记账”的差距
我们使用并观察了“鲨鱼记账”APP,发现,我们的软件真的还有很多不足的地方.就功能这方面来说:“鲨鱼记账”APP有更多的收入.支出分类:就界面来说:“鲨鱼记账”APP有比我们优美太多的页面和背景.但是 ...
- JS 下拉菜单案例
css代码 .nav { width: 300px; height: 400px; list-style: none; padding:; margin: 0 auto; } .nav>li { ...
- 在centos 上安装python
1.下载最新版本python源码包 下载地址为https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz 2.解压源码包 tar -zxf Pyt ...
- Vue 自定义VueRouter-简版
主要是思路,自己定义组件的时候可以借鉴 Vue-router的 类图 name options: ==> 记录构造函数中传入的对象,在 new VueRouter的时候传了一个对象( route ...
- Android小技巧总结——持续更新
WebView实现 博客地址: https://blog.csdn.net/lowprofile_coding/article/details/77928614 获取网络权限 <uses-per ...
- Java实现token的生成与验证-登录功能
一.token与cookie相比较的优势1.支持跨域访问,将token置于请求头中,而cookie是不支持跨域访问的: 2.无状态化,服务端无需存储token,只需要验证token信息是否正确即可,而 ...
- iNeuOS工业互联平台,WEB组态(iNeuView)集成rtmp和websocket视频元件,支持海康、大华等摄像头实时显示视频
目 录 1. 概述... 1 2. 平台演示... 2 3. 硬件摄像头... 2 4. 视频流协议转换管理... 2 5. 组态视频元件 ...
- machine vision plan
以OpenCV+C#/C++为主,Halcon+C#/C++.LabVIEW+NI Vision,其他还不了解 目前:Halcon+C# 1.完成:测量定位,表面质量检测 2.完成1后开始:OpenC ...
- 【论文总结】MapReduce论文
摘要: MR是啥:编程模型,用户只需编写Map,Reduce两个函数,系统完成分布式计算 MR系统是啥:在大量普通计算机上实现并行化计算,系统只关心如何分割数据.大规模集群的调度.集群容错.集群通信 ...
- 构造 IPv6 报文
#!/usr/bin/python from scapy.all import * a=IPv6(nh=58, src='fe80::214:f2ff:fe07:af0', dst='ff02::1' ...
