環境:
- window 10
- .netcore 3.1
- vs2019 16.5.1
- dnspy v6.1.4
參照:
.netcore入門22:使用dnSpy調試asp.net core源碼
asp.net core 系列 17 通用主機 IHostBuilder
官方文檔:.NET 通用主機
一、什麼是通用主機?
通用主機是微軟抽象出來的一個資源容器,它裏面包括了配置模型、日誌框架、依賴容器等。它裏面還包括一些主機服務(IHostedService:存放在依賴注入容器中),這樣當通用主機啓動或停止的時候實際上就是針對這些主機服務進行開啓和停止操作。
二、爲什麼要有通用主機?
在.net core 2.x以前我們創建aspnetcore應用是使用:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
但是在.net core 3.x的時候我們使用如下:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
2.x的那種方式就把主機和web服務捆綁到了一起(其實通過名字IWebHost也能看的出來:主機即web服務),到了3.x微軟就把主機(IHost)和web服務剝離開來:主機裏面可以包含多個主機服務(IHostedService),而web服務只是其中可選的一個服務類型而已。它們的關係看下圖:
此時,我們再來看一下3.x版本中生成的方法,其中Host.CreateDefaultBuilder()…Build().Run()都是針對通用主機的,而web服務是在.ConfigureWebHostDefaults()方法中注入的。
那麼,爲什麼要將主機和web服務剝離開來呢?因爲微軟不想這麼一個框架只能用在web服務上,其他的比如:定時服務,消息隊列,tcp服務器等等,下面就看一個具體的定時服務示例:
三、測試非web服務的通用主機
新建一個空的web項目,將Program中的代碼替換成如下:
public class Program
{
public static void Main(string[] args)
{
new HostBuilder()
.ConfigureServices(services =>
{
services.AddLogging();
services.AddSingleton<IHostedService, MyHostService>();
})
.ConfigureLogging(loggingBuilder =>
{
loggingBuilder.AddConsole();
})
.Build()
.Run();
Console.WriteLine("ok");
Console.ReadLine();
}
}
public class MyHostService : IHostedService
{
private readonly ILogger<MyHostService> logger;
public MyHostService(ILogger<MyHostService> logger)
{
this.logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
while (true)
{
if (cancellationToken.IsCancellationRequested) break;
logger.LogInformation("MyHostService is Comming...");
Thread.Sleep(5000);
}
});
}
public Task StopAsync(CancellationToken cancellationToken)
{
logger.LogInformation("MyHostService is stop!");
return Task.CompletedTask;
}
}
直接運行,看控制檯輸出:
可以看到,我們通過asp.net core 3.x的這種設計,成功的啓動了一個定時服務。
四、通過源碼查看通用主機的構建過程
注意: 這裏說的是通用主機,暫不涉及到web服務(我們在asp.net core中使用的web服務是:GenericWebHostService : IHostedService
)。
4.1 代碼執行過程說明
通用主機的使用流程大概如下:
- 創建“構建者對象”(通用主機的構建者對象:HostBuilder)
- 向“構建者對象”中注入構建邏輯
- 使用“構建者對象”建造“通用主機”(HostBuilder.Build())
- 運行“通用主機” (Host.Run())
- 對於控制檯工程:Ctrl+C 通用主機結束運行,程序結束
通用主機的構建、啓動流程圖:
通過上面的圖,我們可以看到:主機的構建主要是由HostBuilder主導的,那麼我們先來看看HostBuilder
的源碼:
public class HostBuilder : IHostBuilder
{
//屬性堆
public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
//主機配置
private IConfiguration _hostConfiguration;
private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate){...}
//應用配置
private IConfiguration _appConfiguration;
private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate){...}
//依賴注入配置
private IServiceProvider _appServices;
private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate){...}
//主機構建上下文
private HostBuilderContext _hostBuilderContext;
//主機環境
private HostingEnvironment _hostingEnvironment;
......
}
上面是簡化後的代碼,上面的ConfigureHostConfiguration
以及其他的兩個方法都只是將傳進來的委託存放到了對應的集合上(_configureHostConfigActions)。
當程序啓動的時候,代碼通過HostBuilder的Config…方法將一系列的處理邏輯分別存儲了起來,當進行Build()的時候,代碼就會將HostBuilder存儲的這些邏輯執行並從依賴容器中解析出註解對象Host
,最後啓動這個Host(就是啓動Host中的IHostedService)。
4.2 調試代碼,查看執行過程
接下來使用dnSpy調試看一下源碼:
從上圖的Host.CreateDefaultBuilder(args)
調試進去,會看到下圖所示:
上圖中顯示了HostBuidler的配置過程(配置了日誌、依賴容器、json配置等),其實針對通用主機的配置就這麼多。然後看一下,HostBuilder的Build方法:
調試進入Build方法後,我們看到下圖所示:
從上面代碼中我們可以看到,在Build主機的時候我們調用各種構建邏輯(包括日誌、json配置等等,這些邏輯都是在配置的時候由我們或asp.net core框架注入進去的,其中包括將主機服務注入到依賴容器),然後創建出依賴容器,最後從容器中獲取到了主機對象(Host)。
最後我們再看一下主機的啓動過程:
調試進去後(多按幾個F11),我們會看到下圖所示:
從上圖我們可以看到:主機啓動過程就是將主機中的一系列主機服務啓動了。
最後再來看一下HostBuilder的主要內容以及Build過程分析: