asp.net core 3.x 通用主機是如何承載asp.net core的-上

1、前言

上一篇《asp.net core 3.x 通用主機原理及使用》扯了下3.x中的通用主機,恰好有哥們寫了篇《.NET Core 3.1和WorkerServices構建Windows服務》能夠當作通用主機的案例來看。本篇主要聊下asp.net core 3.x中是如何使用通用主機來承載asp.net core自己的。html

注:我是.net framework 4.x跳到.net core 3.x的,基本看源碼總結的,可能某些地方理解不到位,因此此文只做爲參考別全信哈。web

閱讀前提(參考老A的博客):數據庫

  • 瞭解配置系統和選項模式
  • 瞭解通用主機,能夠看看上一篇文章
  • 若是能大概理解Builder模式和上下文模式就更好了

目錄:json

  • 通用主機回顧
  • 通用主機是如何承載asp.net core的
  • 核心類及使用要點
  • 總結

2、回顧通用主機

2.一、IHost表示通用主機

微軟爲咱們提供了一個默認實現(Microsoft.Extensions.Hosting.Internal.Host),它內部主要包含:ioc容器(服務提供器)、生命週期事件處理器、日誌記錄器、和IHostedService集合以及啓動和中止方法。api

2.1.一、ioc服務提供器:

用來存儲各類服務對象,這個根容器是全部IHostedService共享的,各個IHostedService也能夠建立主機的「範圍IOC容器」。ioc容器包含微軟爲咱們塞進去的,和咱們本身塞進去的各類服務組件。在主機配置階段微軟會塞入:緩存

  • HostingEnvironment 主機環境,
    • EnvironmentName主機環境(是開發模式?仍是生產模式?)
    • ApplicationName應用名稱
    • ContentRootPath主機的內容根路徑
    • ContentRootFileProvider內容提供器
  • HostBuilderContext 主機建立過程當中的上下文對象,在配置主機的多個步驟中傳遞數據。主要包含:HostingEnvironment和AppConfiguration應用配置對象
  • AppConfiguration 應用配置對象,裏面也包含主機配置對象
  • IHostLifetime主機生命週期事件處理器
  • IHostApplicationLifetime 應用生命週期事件處理器
  • Internal.Host 做爲默認主機

因此未來咱們定義的類隨時能夠注入這些對象,這些對象是在HostBuilder中賦值的,請往下看app

2.1.二、生命週期事件處理器

未來主機啓動/中止時會觸發回調相應的幾個方法,好比:主機啓動了 應用啓動了  應用中止了,主機中止了。咱們能夠自定義一個事件處理器加入到ioc容器中來實現生命週期事件的訂閱。框架

2.1.三、IHostedService集合

一個IHostedService的實現類就是一個應用。asp.net core自己就是一個應用。asp.net

2.1.四、啓動流程

觸發相應的生命週期事件、遍歷啓動全部IHostedServiceide

2.二、HostBuilder表示主機構造器

它負責主機的配置和生成。它定義了幾個委託集合,並提供相應的方法容許咱們的代碼向集合中加入本身的委託,這些委託主要是用來:

  • 向ioc容器註冊服務;
  • 設置主機和應用配置對象的數據源;
  • 配置容器自己(好比替換成別的依賴注入框架);

未來在調用Build生成最終的主機時會:

  • 建立主機配置生成器ConfigurationBuilder,而後回調咱們的代碼提供的委託對配置對象的數據源進行設置,最終經過配置對象生成器.Build生成配置對象
  • 建立HostingEnvironment(含義上面有說),默認用配置對象進行賦值,而後回調咱們的代碼提供的委託進一步設置主機環境對象
  • 初始化BuilderContext(含義上面有說)
  • 初始化應用配置生成器,套路給主機配置同樣
  • 初始化IOC容器,
    • 先把上面說的對象丟入容器中,
    • 註冊默認主機, Internal.Host 
    • 註冊選項模式須要的幾個服務,
    • 註冊日誌系統須要的服務
    • 而後回調咱們的委託,咱們的委託能夠注入各類主機須要的服務
    • 配置ioc容器自己,咱們對微軟提供的ioc進行配置,或乾脆替換爲第三方依賴注入框架
  • 嘴周從容器中解析獲得主機

2.三、Host.CreateDefaultBuilder進一步簡化主機的配置和建立

  • 建立HostBuilder
  • 設置內容根爲當前應用程序根目錄
  • 將以"DOTNET_"的環境變量和命令行參數(若是有)做爲「主機配置」的數據源
  • 以如下數據做爲「應用配置」的數據源
    • json文件appsettings.json和appsettings.{env.EnvironmentName}.json
    • 若是是開發模式,則添加一個特殊的json文件做爲數據源,這個文件存儲如:帳號 密碼 數據庫鏈接字符串等比較機密的信息(文件查找路徑有點複雜,後期補充)
    • 添加環境變量做爲配置源(沒細看,也許是整個系統的環境變量)
    • 嘗試添加命令行參數配置源
  • 添加日誌記錄系統須要的服務以及相關的配置
  • 使用默認的ServiceProvider,當是開發模式時:在建立對象階段開啓依賴注入範圍驗證

以上爲通用主機的大體內容,先有個印象,未來須要擴展時在研究源碼

3、通用主機是如何承載asp.net core的

asp.net core 3.x開始直接使用通用主機,主要思路是對通用主機作跟web相關配置(添加跟web相關的配置源和註冊服務)關鍵是會將GenericWebHostService註冊到ioc容器中。對於通用主機來講所謂的應用就是一個實現了IHostedService的類。GenericWebHostService就是這樣一個類,它就基本表明了asp.net core。對於通用主機來講asp.net core不過是一個應用而已。未來通用主機啓動時天然是啓動GenericWebHostService

注:GenericWebHostService會在啓動時建立ApplicationBuilder,而後對其進行配置(中間件管道),而後生成一個Application,最後拿到配置好的IServer(如KestrlServer)而後傳入Application並啓動它,Server開始監聽http請求,後續請求抵達時由Application對象負責將請求傳入中間件管道*(大體這麼個流程,還沒詳細去看GenericWebHostService源碼)

先來看個圖:

 核心任務是:

  1. 建立GenericWebHostBuilder,它是對通用主機構建器HostBuilder的一個包裝,提供跟web相關的配置源的設置和服務的註冊。構造函數中會作些初始化,默認配置
  2. 經過靜態方法Host.ConfigureWebDefaults對GenericWebHostBuilder作進一步的默認配置
  3. 調用用戶代碼(咱們本身寫的)對GenericWebHostBuilder作進一步配置

因此文章後面部分咱們將這個用來承載asp.net core的通用主機稱爲「Web主機」,把通用主機配置器HostBuilder的包裝類GenericWebHostBuilder稱爲「Web主機配置器」

4、GenericWebHostBuilder

它包裝了HostBuilder,因此能夠把它理解爲一個特殊的HostBuilder,特殊在它提供web相關的配置api,本質上仍是向通用主機配置對象HostBuilder添加各類服務和配置源

4.一、GetSetting、UseSetting

GenericWebHostBuilder有個IConfiguration類型的屬性,能夠把它理解爲跟web相關的配置對象,它會做爲通用主機的配置源,因爲通用主機的配置源又是應用配置的數據源,所以最後:應用配置對象 = 通用主機配置對象 + (web主機配置對象)GenericWebHostBuilder._config  + 應用配置對象;
_config在構造函數中被初始化,惟一數據源是以"ASPNETCORE_"爲前綴的環境變量
另外配置對象還在ExecuteHostingStartups被使用到,後面會詳細講
GetSetting、UseSetting這倆方法分別從_config讀取和寫入配置,因此咱們能夠在配置主機時更方便的向配置中加入一些值,也能夠替換一些值來影響一些組件的配置

因此咱們能夠在配置web主機時經過UseSetting來快速設置/替換某些配置值,也能夠在任意能得到GenericWebHostBuilder的地方調用GetSetting方便的獲取配置值

4.二、IWebHostEnvironment、WebHostOptions、WebHostBuilderContext

在web主機配置過程當中的多個步驟中常常會使用到這幾個對象,咱們對web主機進行配置時,提供的委託的參數也常常會攜帶者節參數,所以看看這幾個東西是啥。

IWebHostEnvironment
web主機環境相關,它還繼承IHostEnvironment,因此它包含如下內容:

  • EnvironmentName 主機運行環境,是開發?生產?
  • ApplicationName 應用名
  • ContentRootPath 內容根,默認是應用程序所在目錄
  • ContentRootFileProvider 內容根對應的文件提供器
  • WebRootPath  web根,默認wwwroot,
  • WebRootFileProvider web根對應的文件提供器

WebHostOptions
web主機選項對象,雖然是這個名字,可是並無應用選項模式,且它是internal修飾的,所以咱們寫代碼一般不會訪問到它,可是它在GenericWebHostBuilder有些做用的
它內部包含根web相關的配置:

  • ApplicationName:應用程序名
  • PreventHostingStartup、HostingStartupAssemblies、HostingStartupExcludeAssemblies:插件/模塊相關屬性,參考《asp.net core 3.x 模塊化開發之HostingStartup
  • Environment:應用程序當前運行,是開發模式?是生產環境?
  • StartupAssembly:啓動類Startup所在程序集
  • WebRoot:web靜態資源目錄,一般對應那個wwwroot目錄
  • ContentRootPath:內容根路徑,一般對應應用程序所在目錄,

WebHostBuilderContext
= IWebHostEnvironment + IConfiguration

private WebHostBuilderContext GetWebHostBuilderContext(HostBuilderContext context)
GenericWebHostBuilder經過這個方法來建立WebHostBuilderContext,web主機配置器內部多個方法都會調用此方法。此方法執行過程大體步驟以下:

  • 使用主機配置器上下文的配置對象來建立一個WebHostOptions,因此WebHostOptions上面那些屬性都是經過配置來賦值的,因爲HostBuilderContext的配置對象如今=主機配置對象 + 應用配置對象 + web主機配置對象,可想而知,咱們能夠經過多種途徑來配置WebHostOptions的屬性
  • 建立WebHostBuilderContext
    • Configuration = context.Configuration,
    • HostingEnvironment = new HostingEnvironment()
  • 經過HostingEnvironmentExtensions.Initialize對WebHostBuilderContext.HostingEnvironment進行初始化,其實就是將WebHostOptions中相應的屬性賦值上去
  • 最後HostBuilderContext和WebHostOptions被存儲到HostBuilderContext.Properties緩存起來

因此咱們平時能夠經過多種途徑來配置內容根、web根、應用名、運行環境(開發?生成?)
也能夠在配置web主機時的委託中來經過WebHostBuilderContext來訪問到這些屬性和對應的文件提供器
因爲IHostingEnvironment會以單例註冊到容器,所以咱們未來能夠直接注入HostingEnvironment或者web主機環境對象

4.三、public IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure)

調用此方法傳入一個委託,這個委託主要用來配置中間件管道,未來通用主機在啓動時會啓動表明asp.net core的GenericWebHostService,這時咱們這個委託就會被調用。因此配置管道的代碼是在HostBuilder.Build().Run()這個Run階段執行,並非在Build這步

這個方法跟UseStartup(下面會說)是衝突的,意思只能用其中一個,

要了解這個方法的原理,得先說說GenericWebHostServiceOptions,它是一個選項對象,看定義:

 1 namespace Microsoft.AspNetCore.Hosting{
 3     internal class GenericWebHostServiceOptions{
 5        public Action<IApplicationBuilder> ConfigureApplication { get; set; }
 6        public WebHostOptions WebHostOptions { get; set; }
 8        public AggregateException HostingStartupExceptions { get; set; }
10     }
11 }

此對象應用了選項模式,(第5行)ConfigureApplication其實就表明了那個用來配置中間件管道的委託

 1 public IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure){
 2             _builder.ConfigureServices((context, services) => {
 3                 services.Configure<GenericWebHostServiceOptions>(options => {
 4                     var webhostBuilderContext = GetWebHostBuilderContext(context);
 5                     options.ConfigureApplication = app => configure(webhostBuilderContext, app);
 6                 });
 7             });
 8 
 9             return this;
10         }

因此不管是Startup中的Configre方法,仍是這裏傳入的委託,最終都會以一個委託的形式賦值到GenericWebHostServiceOptions.ConfigureApplication屬性上,而這個委託未來在主機啓動階段被調用,最終實現容許用戶配置中間件管道的目的

爲何要提供兩種配置中間件管道的方式呢?由於直接在Program.main裏配置更簡單,可是封裝性很差,經過單獨的Startup類更清晰。

因此咱們配置中間件管道時除了能夠在Startup.Configre中配置,也能夠直接在Program.main裏配置主機時經過GenericWebHostBuilder.Configure進行配置

 

未完待續....

相關文章
相關標籤/搜索