閱讀348 返回首頁    go 阿裏雲 go 技術社區[雲棲]


聊聊ASP.NET Core默認提供的這個跨平台的服務器——KestrelServer

跨平台是ASP.NET Core一個顯著的特性,而KestrelServer是目前微軟推出了唯一一個能夠真正跨平台的Server。KestrelServer利用一個名為KestrelEngine的網絡引擎實現對請求的監聽、接收和響應。KetrelServer之所以具有跨平台的特質,源於KestrelEngine是在一個名為libuv的跨平台網絡庫上開發的。

目錄
一、libuv
二、KestrelServer
三、KestrelServerOptions
四、ApplicationLifetime
五、設置監聽地址

image說起libuv,就不得不談談libev,後者是Unix係統上一個事件循環和事件模型的網絡庫。libev因其具有的高性能成為了繼lievent和Event perl module之後一套最受歡迎的網絡庫。由於Libev不支持Windows,有人在libev之上創建了一個抽象層以屏蔽平台之間的差異,這個抽象層就是libuv。libuv在Windows平台上是采用的形式實現的,右圖揭示了libuv針對Unix和Windows的跨平台實現原理。到目前為止,libuv支持的平台已經不限於Unix和Windows了,包括Linux(2.6)、MacOS和Solaris (121以及之後的版本)在內的平台在libuv支持範圍之內。

如下所示的代碼片段體現了KestrelServer這個類型的定義。除了實現接口IServer定義的Features屬性之外,KestrelServer還具有一個類型為KestrelServerOptions的隻讀屬性Options。這個屬性表示對KestrelServer所作的相關設置,我們在調用構造函數時通過輸入參數options所代表的IOptions<KestrelServerOptions>對象對這個屬性進行初始化。構造函數還具有另兩個額外的參數,它們的類型分別是IApplicationLifetime和ILoggerFactory,後者用於創建記錄日誌的Logger,前者與應用的生命周期管理有關。

   1: public class KestrelServer : IServer
   2: {   
   3:     public IFeatureCollection       Features { get; }
   4:     public KestrelServerOptions     Options { get; }
   5:  
   6:     public KestrelServer(IOptions<KestrelServerOptions> options,IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);
   7:     public void Dispose();
   8:     public void Start<TContext>(IHttpApplication<TContext> application);
   9: }

我們一般通過調用WebHostBuilder的擴展方法UseKestrel方法來完成對KestrelServer的注冊。如下麵的代碼片段所示,UseKestrel方法具有兩個重載,其中一個具有同一個類型為Action<KestrelServerOptions>的參數,我們可以利用這個參數直接完成對KestrelServerOptions的設置。

   1: public static class WebHostBuilderKestrelExtensions
   2: {
   3:     public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder);
   4:     public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<KestrelServerOptions> options);
   5: }


三、KestrelServerOptions

由於Server負責請求的監聽、接收和響應,所以Server是影響整個Web應用響應能力和吞吐量最大的因素之一,為了更加有效地使用Server,我們往往針對具體的網絡負載狀況對其作針對性的設置。對於KestrelServer來說,在構造函數中作為參數指定的KestrelServerOptions對象代表針對它所做的設置。我們針對KestrelServer所做的設置主要體現在KestrelServerOptions類型的如下5個屬性上。

   1: public class KestrelServerOptions
   2: {   
   3:     //省略其他成員
   4:     public int          MaxPooledHeaders { get; set; }
   5:     public int          MaxPooledStreams { get; set; }
   6:     public bool         NoDelay { get; set; }
   7:     public TimeSpan     ShutdownTimeout { get; set; }
   8:     public int          ThreadCount { get; set; }
   9: }

KestrelServerOptions注冊的KetrelServer在管道中會以依賴注入的方式被創建,並采用構造器注入的方式提供其構造函數的參數options,由於這個參數類型為IOptions<KestrelServerOptions>,所以我們利用Options模型以的方式來指定KestrelServerOptions對象承載的設置。比如我們可以將KestrelServer的相關配置定義在如下一個JSON文件中。

   1: { 
   2:     "noDelay"         : false, 
   3:     "shutdownTimeout" : "00:00:10", 
   4:     "threadCount"     : 10 
   5: } 

為了讓應用加載這麼一個配置文件(文件名假設為“KestrelServerOptions.json”),我們隻需要在啟動類型(Startup)類的ConfigureServces方法中按照如下的方式利用ConfigurationBuilder加載這個配置文件並生成相應的Configuration對象,最後按照Options模型的編程方式完成KestrelServerOptions類型和該對象的映射即可。

   1: public class Startup
   2: {
   3:     //其他成員
   4:     public void ConfigureServices(IServiceCollection services)
   5:     {
   6:         IConfiguration configuration = new ConfigurationBuilder()
   7:             .AddJsonFile("KestrelServerOptions.json")
   8:             .Build();
   9:         services.Configure<KestrelServerOptions>(configuration);
  10:     }
  11: }


四、ApplicationLifetime

我們將所有實現了IApplicationLifetime接口的所有類型及其對應對象統稱為ApplicationLifetime。從命名的角度來看,ApplicationLifetime貌似是對當前應用生命周期的描述,而實際上它存在的目的僅僅是在應用啟動和關閉(隻要是關閉)時對相關組件發送通知而已。如下麵的代碼片段所示,IApplicationLifetime接口具有三個CancellationToken類型的屬性(ApplicationStarted、ApplicationStopping和ApplicationStopped),我們可以利用它們是否已經被取消(Cancel)確定當前應用的狀態(已經開啟、正在關閉和已經關閉)。如果試圖關閉應用,StopApplication方法應該被調用以發出應用正在被關閉的通知。對於KestrelServer來說,。

   1: public interface IApplicationLifetime
   2: {
   3:     CancellationToken ApplicationStarted { get; }
   4:     CancellationToken ApplicationStopping { get; }
   5:     CancellationToken ApplicationStopped { get; }
   6:  
   7:     void StopApplication();
   8: }

ASP.NET Core默認使用的ApplicationLifetime是具有如下定義的一個同名類型。可以看出它實現的三個屬性返回的CancellationToken對象是通過三個對應的CancellationTokenSource生成。除了實現IApplicationLifetime接口的StopApplication方法用於發送“正在關閉”通知之外,這個類型還定義了額外兩個方法(NotifyStarted和NotifyStopped)用於發送“已經開啟/關閉”的通知。

   1: public class ApplicationLifetime : IApplicationLifetime
   2: {
   3:     private readonly CancellationTokenSource _startedSource = new CancellationTokenSource();
   4:     private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource();
   5:     private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource();    
   6:  
   7:     public CancellationToken ApplicationStarted
   8:     {
   9:         get { return this._startedSource.Token; }
  10:     }
  11:     public CancellationToken ApplicationStopped
  12:     {
  13:         get { return this._stoppedSource.Token; }
  14:     }
  15:     public CancellationToken ApplicationStopping
  16:     {
  17:         get { return this._stoppingSource.Token; }
  18: }
  19:  
  20:     public void NotifyStarted()
  21:     {
  22:         this._startedSource.Cancel(false);
  23:     }
  24:     public void NotifyStopped()
  25:     {
  26:         this._stoppedSource.Cancel(false);
  27:     }
  28:     public void StopApplication()
  29:     {
  30:         this._stoppingSource.Cancel(false);
  31:     }
  32: }

一個ASP.NET Core應用利用管道處理請求,所以管道的生命周期等同於應用自身的生命周期。當我們調用Run方法開啟WebHost時,請求處理管道被構建出來。。

在演示的實例中,我們實際上並不曾為注冊的KestrelServer指定一個監聽地址,從運行的效果我們不難看出,WebHost在這種情況下會指定“”為默認的監聽地址,Server的監聽地址自然可以顯式指定。在介紹如何通過編程的方式為Server指定監聽地址之前,我們有先來認識一個名為ServerAddressesFeature的特性。

我們知道表示Server的接口IServer中定義了一個類型為IFeatureCollection 的隻讀屬性Features,它表示用於描述當前Server的特性集合,ServerAddressesFeature作為一個重要的特性,就包含在這個集合之中。我們所說的ServerAddressesFeature對象是對所有實現了IServerAddressesFeature接口的所有類型及其對應對象的統稱,該接口具有一個唯一的隻讀屬性返回Server的監聽地址列表。ASP.NET Core默認使用的ServerAddressesFeature是具有如下定義的同名類型。

   1: public interface IServerAddressesFeature
   2: {
   3:     ICollection<string> Addresses { get; }
   4: }
   5:  
   6: public class ServerAddressesFeature : IServerAddressesFeature
   7: {
   8:     public ICollection<string> Addresses { get; }
   9: }

對於WebHost在通過依賴注入的方式創建的Server,由它的Features屬性表示的特性集合中會默認包含這麼一個ServerAddressesFeature對象。如果沒有一個合法的監聽地址被添加到這個 ServerAddressesFeature對象的地址列表中,WebHost會將顯式指定的地址(一個或者多個)添加到該列表中。我們顯式指定的監聽地址實際上是作為WebHost的配置保存在一個Configuration對象上,配置項對應的Key為“”,WebHostDefaults的靜態隻讀屬性ServerUrlsKey返回的就是這麼一個Key。

   1: new WebHostBuilder()
   2:     .UseSetting(WebHostDefaults.ServerUrlsKey, "https://localhost:3721/")
   3:     .UseMyKestrel()
   4:     .UseStartup<Startup>()
   5:     .Build()
   6:      .Run();

WebHost的配置最初來源於創建它的WebHostBuilder,後者提供了一個UseSettings方法來設置某個配置項的值,所以我們可以采用如下的方式來指定監聽地址(“https://localhost:3721/”)。不過,針對監聽地址的顯式設置,最直接的編程方式還是調用WebHostBuilder的擴展方法,如下麵的代碼片段所示,該方法的實現邏輯與上麵完全一致。

   1: public static class WebHostBuilderExtensions
   2: {
   3:     public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls) 
   4:     =>hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls)) ;    
   5: }

作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 12:04:22

  上一篇:go  如何在windows中使用cmd命令去編譯,運行C++程序
  下一篇:go  如果你想深刻理解ASP.NET Core請求處理管道,可以試著寫一個自定義的Server