聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer
跨平臺(tái)是ASP.NET Core一個(gè)顯著的特性,而KestrelServer是目前微軟推出了唯一一個(gè)能夠真正跨平臺(tái)的Server。KestrelServer利用一個(gè)名為KestrelEngine的網(wǎng)絡(luò)引擎實(shí)現(xiàn)對(duì)請(qǐng)求的監(jiān)聽、接收和響應(yīng)。KetrelServer之所以具有跨平臺(tái)的特質(zhì),源于KestrelEngine是在一個(gè)名為libuv的跨平臺(tái)網(wǎng)絡(luò)庫(kù)上開發(fā)的。
目錄
一、libuv
二、KestrelServer
三、KestrelServerOptions
四、ApplicationLifetime
五、設(shè)置監(jiān)聽地址
一、libuv
image說起libuv,就不得不談?wù)刲ibev,后者是Unix系統(tǒng)上一個(gè)事件循環(huán)和事件模型的網(wǎng)絡(luò)庫(kù)。libev因其具有的高性能成為了繼lievent和Event perl module之后一套最受歡迎的網(wǎng)絡(luò)庫(kù)。由于Libev不支持Windows,有人在libev之上創(chuàng)建了一個(gè)抽象層以屏蔽平臺(tái)之間的差異,這個(gè)抽象層就是libuv。libuv在Windows平臺(tái)上是采用IOCP的形式實(shí)現(xiàn)的,右圖揭示了libuv針對(duì)Unix和Windows的跨平臺(tái)實(shí)現(xiàn)原理。到目前為止,libuv支持的平臺(tái)已經(jīng)不限于Unix和Windows了,包括Linux(2.6)、MacOS和Solaris (121以及之后的版本)在內(nèi)的平臺(tái)在libuv支持范圍之內(nèi)。
二、KestrelServer
如下所示的代碼片段體現(xiàn)了KestrelServer這個(gè)類型的定義。除了實(shí)現(xiàn)接口IServer定義的Features屬性之外,KestrelServer還具有一個(gè)類型為KestrelServerOptions的只讀屬性O(shè)ptions。這個(gè)屬性表示對(duì)KestrelServer所作的相關(guān)設(shè)置,我們?cè)谡{(diào)用構(gòu)造函數(shù)時(shí)通過輸入?yún)?shù)options所代表的IOptions<KestrelServerOptions>對(duì)象對(duì)這個(gè)屬性進(jìn)行初始化。構(gòu)造函數(shù)還具有另兩個(gè)額外的參數(shù),它們的類型分別是IApplicationLifetime和ILoggerFactory,后者用于創(chuàng)建記錄日志的Logger,前者與應(yīng)用的生命周期管理有關(guān)。
? ?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: }
我們一般通過調(diào)用WebHostBuilder的擴(kuò)展方法UseKestrel方法來完成對(duì)KestrelServer的注冊(cè)。如下面的代碼片段所示,UseKestrel方法具有兩個(gè)重載,其中一個(gè)具有同一個(gè)類型為Action<KestrelServerOptions>的參數(shù),我們可以利用這個(gè)參數(shù)直接完成對(duì)KestrelServerOptions的設(shè)置。
? ?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負(fù)責(zé)請(qǐng)求的監(jiān)聽、接收和響應(yīng),所以Server是影響整個(gè)Web應(yīng)用響應(yīng)能力和吞吐量最大的因素之一,為了更加有效地使用Server,我們往往針對(duì)具體的網(wǎng)絡(luò)負(fù)載狀況對(duì)其作針對(duì)性的設(shè)置。對(duì)于KestrelServer來說,在構(gòu)造函數(shù)中作為參數(shù)指定的KestrelServerOptions對(duì)象代表針對(duì)它所做的設(shè)置。我們針對(duì)KestrelServer所做的設(shè)置主要體現(xiàn)在KestrelServerOptions類型的如下5個(gè)屬性上。
? ?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注冊(cè)的KetrelServer在管道中會(huì)以依賴注入的方式被創(chuàng)建,并采用構(gòu)造器注入的方式提供其構(gòu)造函數(shù)的參數(shù)options,由于這個(gè)參數(shù)類型為IOptions<KestrelServerOptions>,所以我們利用Options模型以配置的方式來指定KestrelServerOptions對(duì)象承載的設(shè)置。比如我們可以將KestrelServer的相關(guān)配置定義在如下一個(gè)JSON文件中。
? ?1: {?
? ?2: ? ? "noDelay" ? ? ? ? : false,?
? ?3: ? ? "shutdownTimeout" : "00:00:10",?
? ?4: ? ? "threadCount" ? ? : 10?
? ?5: }?
為了讓應(yīng)用加載這么一個(gè)配置文件(文件名假設(shè)為“KestrelServerOptions.json”),我們只需要在啟動(dòng)類型(Startup)類的ConfigureServces方法中按照如下的方式利用ConfigurationBuilder加載這個(gè)配置文件并生成相應(yīng)的Configuration對(duì)象,最后按照Options模型的編程方式完成KestrelServerOptions類型和該對(duì)象的映射即可。
? ?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
我們將所有實(shí)現(xiàn)了IApplicationLifetime接口的所有類型及其對(duì)應(yīng)對(duì)象統(tǒng)稱為ApplicationLifetime。從命名的角度來看,ApplicationLifetime貌似是對(duì)當(dāng)前應(yīng)用生命周期的描述,而實(shí)際上它存在的目的僅僅是在應(yīng)用啟動(dòng)和關(guān)閉(只要是關(guān)閉)時(shí)對(duì)相關(guān)組件發(fā)送通知而已。如下面的代碼片段所示,IApplicationLifetime接口具有三個(gè)CancellationToken類型的屬性(ApplicationStarted、ApplicationStopping和ApplicationStopped),我們可以利用它們是否已經(jīng)被取消(Cancel)確定當(dāng)前應(yīng)用的狀態(tài)(已經(jīng)開啟、正在關(guān)閉和已經(jīng)關(guān)閉)。如果試圖關(guān)閉應(yīng)用,StopApplication方法應(yīng)該被調(diào)用以發(fā)出應(yīng)用正在被關(guān)閉的通知。對(duì)于KestrelServer來說,如果請(qǐng)求處理線程中發(fā)生未被處理異常,它會(huì)調(diào)用這個(gè)方法。
? ?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默認(rèn)使用的ApplicationLifetime是具有如下定義的一個(gè)同名類型。可以看出它實(shí)現(xiàn)的三個(gè)屬性返回的CancellationToken對(duì)象是通過三個(gè)對(duì)應(yīng)的CancellationTokenSource生成。除了實(shí)現(xiàn)IApplicationLifetime接口的StopApplication方法用于發(fā)送“正在關(guān)閉”通知之外,這個(gè)類型還定義了額外兩個(gè)方法(NotifyStarted和NotifyStopped)用于發(fā)送“已經(jīng)開啟/關(guān)閉”的通知。
? ?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: }
一個(gè)ASP.NET Core應(yīng)用利用管道處理請(qǐng)求,所以管道的生命周期等同于應(yīng)用自身的生命周期。當(dāng)我們調(diào)用Run方法開啟WebHost時(shí),請(qǐng)求處理管道被構(gòu)建出來。如果管道在處理請(qǐng)求時(shí)發(fā)生未被處理的異常,管道的Sever會(huì)調(diào)用ApplicationLifeTime對(duì)象的StopApplication方法向WebHost發(fā)送關(guān)閉應(yīng)用的通知以便后者執(zhí)行一些回收釋放工作。
五、設(shè)置監(jiān)聽地址
在演示的實(shí)例中,我們實(shí)際上并不曾為注冊(cè)的KestrelServer指定一個(gè)監(jiān)聽地址,從運(yùn)行的效果我們不難看出,WebHost在這種情況下會(huì)指定“http://localhost:5000”為默認(rèn)的監(jiān)聽地址,Server的監(jiān)聽地址自然可以顯式指定。在介紹如何通過編程的方式為Server指定監(jiān)聽地址之前,我們有先來認(rèn)識(shí)一個(gè)名為ServerAddressesFeature的特性。
我們知道表示Server的接口IServer中定義了一個(gè)類型為IFeatureCollection 的只讀屬性Features,它表示用于描述當(dāng)前Server的特性集合,ServerAddressesFeature作為一個(gè)重要的特性,就包含在這個(gè)集合之中。我們所說的ServerAddressesFeature對(duì)象是對(duì)所有實(shí)現(xiàn)了IServerAddressesFeature接口的所有類型及其對(duì)應(yīng)對(duì)象的統(tǒng)稱,該接口具有一個(gè)唯一的只讀屬性返回Server的監(jiān)聽地址列表。ASP.NET Core默認(rèn)使用的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: }
對(duì)于WebHost在通過依賴注入的方式創(chuàng)建的Server,由它的Features屬性表示的特性集合中會(huì)默認(rèn)包含這么一個(gè)ServerAddressesFeature對(duì)象。如果沒有一個(gè)合法的監(jiān)聽地址被添加到這個(gè) ServerAddressesFeature對(duì)象的地址列表中,WebHost會(huì)將顯式指定的地址(一個(gè)或者多個(gè))添加到該列表中。我們顯式指定的監(jiān)聽地址實(shí)際上是作為WebHost的配置保存在一個(gè)Configuration對(duì)象上,配置項(xiàng)對(duì)應(yīng)的Key為“server.urls”,WebHostDefaults的靜態(tài)只讀屬性ServerUrlsKey返回的就是這么一個(gè)Key。
? ?1: new WebHostBuilder()
? ?2: ? ? .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:3721/")
? ?3: ? ? .UseMyKestrel()
? ?4: ? ? .UseStartup<Startup>()
? ?5: ? ? .Build()
? ?6: ? ? ?.Run();
WebHost的配置最初來源于創(chuàng)建它的WebHostBuilder,后者提供了一個(gè)UseSettings方法來設(shè)置某個(gè)配置項(xiàng)的值,所以我們可以采用如下的方式來指定監(jiān)聽地址(“http://localhost:3721/”)。不過,針對(duì)監(jiān)聽地址的顯式設(shè)置,最直接的編程方式還是調(diào)用WebHostBuilder的擴(kuò)展方法UseUrls,如下面的代碼片段所示,該方法的實(shí)現(xiàn)邏輯與上面完全一致。
? ?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: }
通過一個(gè)Hello World程序來認(rèn)識(shí)一下ASP.NET Core的請(qǐng)求處理管道
創(chuàng)建一個(gè)“迷你版”的管道模擬ASP.NET Core管道對(duì)HTTP請(qǐng)求的處理流程
通過模擬管道為你演示ASP.NET Core管道是如何處理HTTP請(qǐng)求的
ASP.NET Core的HTTP請(qǐng)求處理管道是如何一步步建立起來的?
學(xué)習(xí)ASP.NET Core,你必須知道“中間件”是什么?中間件如何注冊(cè)?請(qǐng)求處理管道是如何通過中間件構(gòu)建的?
ASP.NET Core 管道= 服務(wù)器+ HttpApplication,HttpApplication是個(gè)什么玩意兒?
請(qǐng)掃描此二維碼或者搜索“大內(nèi)老A”關(guān)注蔣金楠(Artech)微信公眾帳號(hào),你將會(huì)得到及時(shí)的高質(zhì)量技術(shù)文章推送信息。
內(nèi)容轉(zhuǎn)載自公眾號(hào)
大內(nèi)老A 了解更多總結(jié)
以上是生活随笔為你收集整理的聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio Code五月
- 下一篇: Roslyn项目系统简介