ASP.NET CORE 啓動過程及源碼解讀

疑問

剛剛接觸ASP.NET CORE 項目的同學可能會有如下疑問:

    1.  ASP.NET CORE 項目的啓動過程是怎麼樣的?

    2.爲什麼ASP.NET CORE項目可以在控制檯中運行啓動後變成了一個網站程序?

現在我這裏使用.NETCORE 3.1 最新穩定發佈版本來進行以上問題的解析,帶大家解決以上問題的疑惑,學習完大家會對ASP.NETCORE 項目會有一個不一樣的理解和領悟.

啓動過程

    剛剛接觸ASP.NET core 的同學們估計都會覺得和之前的ASP.NET 設計大不一樣,代碼風格也有很大的變化,以前的ASP.NET 是全家桶框架模式,裏面包含了所有的實現,你用的到的用不到的都集成在裏面;然而ASP.NET CORE 框架做了大的改變,以最小化抽象設計,通過擴展方法完成易用性擴展.

    解讀過源代碼的同學們都可以發現大多api都是最小化單元抽象接口方式進行設計,其他複雜的方法api都是通過擴展方法進行擴展提供,這也是.NET Core 高效易擴展的一大優勢原因.

     對於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.NETCORE WEB 項目 步驟如下

    文件-> 新建 -> 項目 -> 選擇ASP.Net Core Web應用程序 -> 選擇.NETCORE 3.1 框架 如圖:

創建項目後我們從Program 類中可以看到以下代碼:

public class Program{        public static void Main(string[] args)        {            CreateHostBuilder(args).Build().Run();        }        public static IHostBuilder CreateHostBuilder(string[] args) =>            Host.CreateDefaultBuilder(args)//開啓一個默認的通用主機Host建造者                .ConfigureWebHostDefaults(webBuilder =>                {                    webBuilder.UseStartup<Startup>();                });}
查看以上代碼可以發現 Main 方法中代碼很簡單 ,清晰可見

     CreateHostBuilder(args) :方法創建了一個IHostBuilder 抽象對象,創建過程包含CreateDefaultBuilder(args) :開啓創建一個默認的通用宿主機Host建造者,再通過ConfigureWebHostDefaults()方法配置開啓默認的Kestrel 爲默認的Web服務器並對其進行默認配置,並集成對iis的集成

     Build() :負責創建IHost,看過源代碼的同學可以發現Build的過程 會配置各種東西,本身通過管道模式進行了一系列的默認或者自定義的配置以及服務註冊的構建(下面會詳細講解)

     Run() :啓動Host

所以,ASP.NET Core應用的啓動本質上是啓動作爲宿主的Host對象,

其主要涉及到兩個關鍵對象IHostBuilder和IHost,它們的內部實現是ASP.NET Core應用的核心所在。下面我們就結合源碼並梳理調用堆棧來一探究竟!

源代碼詳細圖如下:

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

     UseContentRoot:指定Web host使用的content root(內容根目錄),比如Views。默認爲當前應用程序根目錄。

    ConfigureHostConfiguration :啓動時宿主機需要的環境變量等相關,支持命令行

    ConfigureAppConfiguration:設置當前應用程序配置。主要是讀取 appsettinggs.json 配置文件、開發環境中配置的UserSecrets、添加環境變量和命令行參數 。

    ConfigureLogging:讀取配置文件中的Logging節點,配置日誌系統。

    UseDefaultServiceProvider:設置默認的依賴注入容器。

 

從圖中可以看出CreateDefaultBuilder 後調用了ConfigureWebHostDefaults 方法,該方法默認主要做了以下幾個事情

   UseStaticWebAssets:靜態文件環境的配置啓用

   UseKestrel:開啓Kestrel爲默認的web 服務器.

   ConfigureServices:服務中間件的註冊,包含路由的中間件的註冊

   UseIIS:對iis 集成的支持

   UseStartup:程序Startup 啓動,該啓動類中可以註冊中間件、擴展第三方中間件,以及相關應用程序配置的處理等等操作

現在所有的配置都已經配置創建好了,接下來我們來看看Build 方法主要做了哪些不爲人知的事情,先來看下源代碼

        /// <summary>        /// Run the given actions to initialize the host. This can only be called once.        /// </summary>        /// <returns>An initialized <see cref="IHost"/></returns>        public IHost Build()        {            if (_hostBuilt)            {                throw new InvalidOperationException("Build can only be called once.");            }            _hostBuilt = true;                        BuildHostConfiguration();            CreateHostingEnvironment();            CreateHostBuilderContext();            BuildAppConfiguration();            CreateServiceProvider();            return _appServices.GetRequiredService<IHost>();        }

從代碼中可以發現有一個_hostBuilt 的變量,細心的同學可以發現該變量主要是用於控制是否build 過,所以這裏可以大膽猜測只能build 一次該Host;現在看下源代碼解析圖:

經過查看源代碼得到的執行結構如上,因此我把代碼改造成如下結構。

public class Program{        public static void Main(string[] args)        {            CreateHostBuilder(args).Build().Run();        }        public static IHostBuilder CreateHostBuilder(string[] args) =>            Host.CreateDefaultBuilder(args)//開啓一個默認的通用主機Host建造者                .ConfigureAppConfiguration(config => {                    //註冊應用程序內所使用的配置文件,比如數據庫鏈接等等                    Console.WriteLine("ConfigureAppConfiguration");                })                .ConfigureServices(service =>                {                    //註冊服務中間件等操作                    Console.WriteLine("ConfigureServices");                })                .ConfigureHostConfiguration(builder => {                    //啓動時需要的組件配置等,比如監聽的端口 url地址等                   Console.WriteLine("ConfigureHostCOnfiguration");                })                .ConfigureWebHostDefaults(webBuilder =>                {                    webBuilder.UseStartup<Startup>();                });}

Startup 代碼如下:​​​​​​​

public class Startup    {        public Startup(IConfiguration configuration)        {            Configuration = configuration;            Console.WriteLine("Startup ");        }        public IConfiguration Configuration { get; }        // This method gets called by the runtime. Use this method to add services to the container.        public void ConfigureServices(IServiceCollection services)        {            Console.WriteLine("ConfigureServices");            services.AddControllersWithViews();        }        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)        {            Console.WriteLine("Configure");            if (env.IsDevelopment())            {                app.UseDeveloperExceptionPage();            }            else            {                app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. 
//You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.                app.UseHsts();            }            app.UseHttpsRedirection();            app.UseStaticFiles();            app.UseRouting();            app.UseAuthorization();            app.UseEndpoints(endpoints =>            {                endpoints.MapControllerRoute(                    name: "default",                    pattern: "{controller=Home}/{action=Index}/{id?}");            });        }    }}

通過執行發現執行代碼順序正如我們源代碼的執行順序一致,執行順序如下圖:

 

爲何通過控制檯命令運行後自動啓動了一個網站程序?

    以前ASP.NET web項目是需要搭建在iis 中託管運行,但是ASP.NETCORE 項目可以直接通過命令行進行託管運行,運行後可以直接瀏覽器打開,你們有沒有考慮過爲什麼?,細心的同學查看項目屬性也會發現項目的輸出類型也是控制檯項目,如圖:

查看這圖,有沒有發現很神奇,爲什麼輸出類型竟然可以通過控制檯命令行進行啓動項目呢?

在上面的源代碼分析過程中可以發現啓動時會啓動一個Kestrel 服務器(ConfigureWebHostDefaults方法中會調用UseKestrel),所以命令後啓動一個控制檯應用程序後相當於啓動了一臺web服務器;下面簡要的概括下Kestrel 服務器的優勢:

Kestrel:Kestrel 是個精簡高效的 HttpServer,以包形式提供,自身不能單獨運行。內部封裝了對 libuv 的調用,作爲I/O底層,屏蔽各系統底層實現差異;有了Kestrel才能真正的實現跨平臺.

好了,想必同學們到這裏已經對上面 兩個疑惑有了清晰的答案了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章