跨平臺是ASP.NET Core一個顯著的特性,而KestrelServer是目前微軟推出了唯一一個能夠真正跨平臺的Server。KestrelServer利用一個名為KestrelEngine的網絡引擎實現對請求的監聽、接收和響應。KetrelServer之所以具有跨平臺的特質,源于KestrelEngine是在一個名為libuv的跨平臺網絡庫上開發的。
目錄
一、libuv
二、KestrelServer
三、KestrelServerOptions
四、ApplicationLifetime
五、設置監聽地址
一、libuv
說起libuv,就不得不談談libev,后者是Unix系統上一個事件循環和事件模型的網絡庫。libev因其具有的高性能成為了繼lievent和Event perl module之后一套最受歡迎的網絡庫。由于Libev不支持Windows,有人在libev之上創建了一個抽象層以屏蔽平臺之間的差異,這個抽象層就是libuv。libuv在Windows平臺上是采用IOCP的形式實現的,右圖揭示了libuv針對Unix和Windows的跨平臺實現原理。到目前為止,libuv支持的平臺已經不限于Unix和Windows了,包括Linux(2.6)、MacOS和Solaris (121以及之后的版本)在內的平臺在libuv支持范圍之內。
二、KestrelServer
如下所示的代碼片段體現了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時,請求處理管道被構建出來。如果管道在處理請求時發生未被處理的異常,管道的Sever會調用ApplicationLifeTime對象的StopApplication方法向WebHost發送關閉應用的通知以便后者執行一些回收釋放工作。
五、設置監聽地址
在演示的實例中,我們實際上并不曾為注冊的KestrelServer指定一個監聽地址,從運行的效果我們不難看出,WebHost在這種情況下會指定“http://localhost:5000”為默認的監聽地址,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為“server.urls”,WebHostDefaults的靜態只讀屬性ServerUrlsKey返回的就是這么一個Key。
1: new WebHostBuilder()
2: .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:3721/")
3: .UseMyKestrel()
4: .UseStartup<Startup>()
5: .Build()
6: .Run();
WebHost的配置最初來源于創建它的WebHostBuilder,后者提供了一個UseSettings方法來設置某個配置項的值,所以我們可以采用如下的方式來指定監聽地址(“http://localhost:3721/”)。不過,針對監聽地址的顯式設置,最直接的編程方式還是調用WebHostBuilder的擴展方法UseUrls,如下面的代碼片段所示,該方法的實現邏輯與上面完全一致。
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: }
文章列表