一張圖理清ASP.NET Core啟動流程

風靈使發表於2019-01-04

1. 引言

對於ASP.NET Core應用程式來說,我們要記住非常重要的一點是:其本質上是一個獨立的控制檯應用,它並不是必需在IIS內部託管且並不需要IIS來啟動執行(而這正是ASP.NET Core跨平臺的基石)。ASP.NET Core應用程式擁有一個內建的Self-Hosted(自託管)的Web Server(Web伺服器),用來處理外部請求。

不管是託管還是自託管,都離不開Host(宿主)。在ASP.NET Core應用中通過配置並啟動一個Host來完成應用程式的啟動和其生命週期的管理(如下圖所示)。而Host的主要的職責就是Web Server的配置和Pilpeline(請求處理管道)的構建。
ASP.NET Core總體啟動流程

這張圖描述了一個總體的啟動流程,從上圖中我們知道ASP.NET Core應用程式的啟動主要包含三個步驟:

  1. CreateDefaultBuilder():建立IWebHostBuilder
  2. Build()IWebHostBuilder負責建立IWebHost
  3. Run():啟動IWebHost

所以,ASP.NET Core應用的啟動本質上是啟動作為宿主的WebHost物件。
其主要涉及到兩個關鍵物件IWebHostBuilderIWebHost,它們的內部實現是ASP.NET Core應用的核心所在。下面我們就結合原始碼並梳理呼叫堆疊來一探究竟!

2. 宿主構造器:IWebHostBuilder

在啟動IWebHost宿主之前,我們需要完成對IWebHost的建立和配置。而這一項工作需要藉助IWebHostBuilder物件來完成的,ASP.NET Core中提供了預設實現WebHostBuilder。而WebHostBuilder是由WebHost的同名工具類(Microsoft.AspNetCore名稱空間下)中的CreateDefaultBuilder方法建立的。
在這裡插入圖片描述
CreateDefaultBuilder()呼叫堆疊

從上圖中我們可以看出CreateDefaultBuilder()方法主要乾了六件大事:

  1. UseKestrel:使用Kestrel作為Web server
  2. UseContentRoot:指定Web host使用的content
    root(內容根目錄),比如Views。預設為當前應用程式根目錄。
  3. ConfigureAppConfiguration:設定當前應用程式配置。主要是讀取 appsettinggs.json配置檔案、開發環境中配置的UserSecrets、新增環境變數和命令列引數 。
  4. ConfigureLogging:讀取配置檔案中的Logging節點,配置日誌系統。
  5. UseIISIntegration:使用IISIntegration 中介軟體。
  6. UseDefaultServiceProvider:設定預設的依賴注入容器。

建立完畢WebHostBuilder後,通過呼叫UseStartup()來指定啟動類,來為後續服務的註冊及中介軟體的註冊提供入口。

3. 宿主:IWebHost

在ASP.Net Core中定義了IWebHost用來表示Web應用的宿主,並提供了一個預設實現WebHost。宿主的建立是通過呼叫IWebHostBuilderBuild()方法來完成的。那該方法主要做了哪些事情呢,我們來看下面這張【ASP.NET Core啟動流程呼叫堆疊】中的黃色邊框部分:
在這裡插入圖片描述
ASP.NET Core啟動流程呼叫堆疊

其核心主要在於WebHost的建立,又可以劃分為三個部分:

  1. 構建依賴注入容器,初始通用服務的註冊:BuildCommonService();
  2. 例項化WebHostvar host = new WebHost(...);
  3. 初始化WebHost,也就是構建由中介軟體組成的請求處理管道:host.Initialize();

3.1. 註冊初始通用服務

BuildBuildCommonService方法主要做了兩件事:

  1. 查詢HostingStartupAttribute特性以應用其他程式集中的啟動配置
  2. 註冊通用服務
  3. 若配置了啟動程式集,則發現並以IStartup型別注入到IOC容器中

3.2. 建立IWebHost

public IWebHost Build()
{
    //省略部分程式碼

    var host = new WebHost(
        applicationServices,
        hostingServiceProvider,
        _options,
        _config,
        hostingStartupErrors);
    }
    
    host.Initialize();

    return host;
}

3.3. 構建請求處理管道

請求管道的構建,主要是中介軟體之間的銜接處理。

而請求處理管道的構建,又包含三個主要部分:

  1. 註冊Startup中繫結的服務;
  2. 配置IServer
  3. 構建管道

請求管道的構建主要是藉助於IApplicationBuilder,相關類圖如下:
在這裡插入圖片描述
請求管道的構建

4. 啟動WebHost

WebHost的啟動主要分為兩步:

  1. 再次確認請求管道正確建立
  2. 啟動Server以監聽請求
  3. 啟動 HostedService
    在這裡插入圖片描述
    啟動WebHost呼叫堆疊

4.1. 確認請求管道的建立

從圖中可以看出,第一步呼叫Initialize()方法主要是取保請求管道的正確建立。其內部主要是對BuildApplication()方法的呼叫,與我們上面所講WebHost的構建環節具有相同的呼叫堆疊。而最終返回的正是由中介軟體銜接而成的RequestDelegate型別代表的請求管道。

4.2. 啟動Server

我們先來看下類圖:
在這裡插入圖片描述
IServer類圖

從類圖中我們可以看出IServer介面主要定義了一個只讀的特性集合屬性、一個啟動和停止的方法宣告。在建立宿主構造器IWebHostBuilder時我們通過呼叫UseKestrel()方法指定了使用KestrelServer作為預設的IServer實現。其方法申明中接收了一個IHttpApplication<TContext> application的引數,從命名來看,它代表一個Http應用程式,我們來看下具體的介面定義:
在這裡插入圖片描述
IHttpApplication類圖

其主要定義了三個方法,第一個方法用來建立請求上下文;第二個方法用來處理請求;第三個方法用來釋放上下文。而至於請求上下文,是用來攜帶請求和返回響應的核心引數,其貫穿與整個請求處理管道之中。ASP.NET Core中提供了預設的實現HostingApplication,其建構函式接收一個RequestDelegate _application(也就是連結中介軟體形成的處理管道)用來處理請求。

var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();
var hostingApp = new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory);

4.3. 啟動IHostedService

IHostedService介面用來定義後臺任務,通過實現該介面並註冊到Ioc容器中,它會隨著ASP.NET Core 程式啟動而啟動,終止而終止。

5. 總結

結合原始碼,通過對ASP.NET Core執行呼叫堆疊的梳理,其啟動流程的總體脈絡一目瞭然,並且瞭解到主要的幾個關鍵物件:

  1. 負責建立IWebHost的宿主構造器IWebHostBuilder
  2. 代表宿主的IWebHost介面
  3. 用於構建請求管道的IApplicationBuilder
  4. 中介軟體銜接而成的RequestDelegate
  5. 代表Web ServerIServer介面
  6. 貫穿請求處理管道的請求上下文HttpContext
  7. 可以用來註冊後臺服務的IHostedService介面

相關文章