OS:Ubuntu 18.04 64bit
.NET Core SDK Version: 3.1.101
源代碼版本:release/3.1
Kestrel的本質(zhì) - Web ServerKestrel是http://ASP.NET Core框架" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

18143453325 在線咨詢 在線咨詢
18143453325 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 深入ASP.NET Core源代碼之 - Web Server Kestrel

深入ASP.NET Core源代碼之 - Web Server Kestrel

時間:2023-05-03 03:27:02 | 來源:網(wǎng)站運營

時間:2023-05-03 03:27:02 來源:網(wǎng)站運營

深入ASP.NET Core源代碼之 - Web Server Kestrel:
測試代碼運行環(huán)境:
OS:Ubuntu 18.04 64bit
.NET Core SDK Version: 3.1.101
源代碼版本:release/3.1

Kestrel的本質(zhì) - Web Server


Kestrel是http://ASP.NET Core框架內(nèi)置的默認(rèn)Web Server 什么是Web Server? 根據(jù)維基百科的定義: Web Server是可以處理來自客戶端的HTTP協(xié)議請求并返回網(wǎng)頁的軟件或硬件。 因此Kestrel的主要功能就是接收來自網(wǎng)絡(luò)客戶端的HTTP請求,并根據(jù)請求返回對應(yīng)的網(wǎng)頁(數(shù)據(jù)也是一種網(wǎng)頁)。

定義 - IServer、IHttpApplication<TContext>


http://ASP.NET Core定義了兩個基本的接口IServer,及IHttpApplication<TContext>,IServer接口定義了Web Server的基本功能,IHttpApplication<TContext>則定義了處理HTTP協(xié)議的應(yīng)用程序的基本功能,我們首先來看下這兩個定義:

Web 服務(wù)器 - IServer

namespace Microsoft.AspNetCore.Hosting.Server{ /// <summary> /// Represents a server. /// </summary> public interface IServer : IDisposable { /// <summary> /// A collection of HTTP features of the server. /// </summary> IFeatureCollection Features { get; } /// <summary> /// Start the server with an application. /// </summary> /// <param name="application">An instance of <see cref="IHttpApplication{TContext}"/>.</param> /// <typeparam name="TContext">The context associated with the application.</typeparam> /// <param name="cancellationToken">Indicates if the server startup should be aborted.</param> Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken); /// <summary> /// Stop processing requests and shut down the server, gracefully if possible. /// </summary> /// <param name="cancellationToken">Indicates if the graceful shutdown should be aborted.</param> Task StopAsync(CancellationToken cancellationToken); }}Features 是一個功能集合,其中可以包含所有應(yīng)用程序需要的,用以處理HTTP協(xié)議各個階段和組成部分的功能集,以接口的形式注入到Features中。

StartAsync方法可以啟動IServer對象,用來接受用戶請求。包含兩個參數(shù):IHttpApplication<TContext>CancellationToken。 IHttpApplicatoin<TContext>是最終處理HTTP請求的應(yīng)用程序入口點,在ASP.NET Core應(yīng)用程序中,默認(rèn)的IHttpApplication<TContext>實現(xiàn)是:HostingApplication,我們會在稍后的部分進行詳細的介紹。 而CancellationToken用來響應(yīng)中斷應(yīng)用程序啟動的請求。

StopAsync方法用來處理停止服務(wù)的請求,接受一個參數(shù)CancellationToken,用來響應(yīng)中斷停止應(yīng)用程序的請求。

Http應(yīng)用程序 - IHttpApplication<TContext>

namespace Microsoft.AspNetCore.Hosting.Server{ /// <summary> /// Represents an application. /// </summary> /// <typeparam name="TContext">The context associated with the application.</typeparam> public interface IHttpApplication<TContext> { /// <summary> /// Create a TContext given a collection of HTTP features. /// </summary> /// <param name="contextFeatures">A collection of HTTP features to be used for creating the TContext.</param> /// <returns>The created TContext.</returns> TContext CreateContext(IFeatureCollection contextFeatures); /// <summary> /// Asynchronously processes an TContext. /// </summary> /// <param name="context">The TContext that the operation will process.</param> Task ProcessRequestAsync(TContext context); /// <summary> /// Dispose a given TContext. /// </summary> /// <param name="context">The TContext to be disposed.</param> /// <param name="exception">The Exception thrown when processing did not complete successfully, otherwise null.</param> void DisposeContext(TContext context, Exception exception); }}IHttpApplication<TContext>接口的定義包含了三個方法: CreateContext方法用來創(chuàng)建處理請求的上下文中所需要的所有相關(guān)數(shù)據(jù),組成Context對象,由接口的實現(xiàn)自己定義類型, ProcessRequestAsync方法使用CreateContext方法創(chuàng)建的Context對象處理本次請求。 DisposeContext方法在完成請求的處理后,負(fù)責(zé)釋放Context對象。

實現(xiàn) - KestrelServer

http://ASP.NET Core提供了默認(rèn)的IServerKestrelServer,下面我們就來看看KestrelServer具體都做了些什么。

KestrelServer 定義在dotnet/aspnetcore項目中(GITHUB REPO)。 項目名稱為:Microsoft.AspNetCore.Server.Kestrel.Core 名稱空間:Microsoft.AspNetCore.Server.Kestrel.Core 源代碼

服務(wù)器啟動:端口監(jiān)聽,協(xié)議解析及請求處理。

我們先看一下KestrelServer.StartAsync()方法的代碼實現(xiàn):

public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) { try { if (!BitConverter.IsLittleEndian) { throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported); } ValidateOptions(); if (_hasStarted) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted); } _hasStarted = true; ServiceContext.Heartbeat?.Start(); async Task OnBind(ListenOptions options) { // Add the HTTP middleware as the terminal connection middleware options.UseHttpServer(ServiceContext, application, options.Protocols); var connectionDelegate = options.Build(); // Add the connection limit middleware if (Options.Limits.MaxConcurrentConnections.HasValue) { connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync; } var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate); var transport = await _transportFactory.BindAsync(options.EndPoint).ConfigureAwait(false); // Update the endpoint options.EndPoint = transport.EndPoint; var acceptLoopTask = connectionDispatcher.StartAcceptingConnections(transport); _transports.Add((transport, acceptLoopTask)); } await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false); } catch (Exception ex) { Trace.LogCritical(0, ex, "Unable to start Kestrel."); Dispose(); throw; } }Kestrel首先會檢查服務(wù)器的字節(jié)序,目前是不支持大端序的。 然后檢查最大請求長度限制的設(shè)置項,以及服務(wù)器是否已經(jīng)啟動。

最后,通過AddressBinder對預(yù)先配置的IP地址或終結(jié)點(EndPoint)名稱進行監(jiān)聽,開始接受客戶端的請求。

當(dāng)每有一個新的HTTP請求通過TCP協(xié)議或其他協(xié)議和服務(wù)器成功簡歷連接后,AddressBinder使用ThreadPool.UnsafeQueueUserWorkItem()方法將OnBind()方法添加到線程池中,等待線程池的調(diào)度。

如果此時進程有可用的線程,就會調(diào)用OnBind()方法,處理用戶的HTTP請求。

OnBind()方法默認(rèn)使用HttpConnectionMiddleware<ServiceContext>中間件,處理新接入的用戶請求,當(dāng)設(shè)置了MaxConcurrentConnections值為True時,則會默認(rèn)使用ConnectionLimitMiddleware中間件,限制最大可用連接數(shù),如果當(dāng)前請求數(shù)已經(jīng)達到最大可接受連接數(shù),則拒絕用戶的請求并斷開連接,否則調(diào)用HttpConnectionMiddleware<ServiceContext>中間件,繼續(xù)處理用戶的請求。

處理HTTP請求 - HttpConnectionMiddleware<ServiceContext>HttpConnection

HttpConnectionMiddleware<ServiceContext>中間件負(fù)責(zé)組裝連接相關(guān)的上下文數(shù)據(jù)HttpConnectionContext,并使用HttpConnection類處理用戶請求。

internal class HttpConnectionMiddleware<TContext> { private readonly ServiceContext _serviceContext; private readonly IHttpApplication<TContext> _application; private readonly HttpProtocols _protocols; public HttpConnectionMiddleware(ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols) { _serviceContext = serviceContext; _application = application; _protocols = protocols; } public Task OnConnectionAsync(ConnectionContext connectionContext) { var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>(); var httpConnectionContext = new HttpConnectionContext { ConnectionId = connectionContext.ConnectionId, ConnectionContext = connectionContext, Protocols = _protocols, ServiceContext = _serviceContext, ConnectionFeatures = connectionContext.Features, MemoryPool = memoryPoolFeature.MemoryPool, Transport = connectionContext.Transport, LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint, RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint }; var connection = new HttpConnection(httpConnectionContext); return connection.ProcessRequestsAsync(_application); } }

HTTP版本控制 - HttpConnection

當(dāng)用戶創(chuàng)建HttpConnection類時,在初始化過程中,會根據(jù)用戶請求聲明的HTTP協(xié)議版本,分別創(chuàng)建對應(yīng)版本的Connection類,并使用該類處理用戶請求:

public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication) { try { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNowTicks); IRequestProcessor requestProcessor = null; switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection<TContext>(_context); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(_context); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; if (requestProcessor != null) { var connectionHeartbeatFeature = _context.ConnectionFeatures.Get<IConnectionHeartbeatFeature>(); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get<IConnectionLifetimeNotificationFeature>(); // These features should never be null in Kestrel itself, if this middleware is ever refactored to run outside of kestrel, // we'll need to handle these missing. Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); // Register the various callbacks once we're going to start processing requests // The heart beat for various timeouts connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); // Register for graceful shutdown of the server using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this); // Register for connection close using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnConnectionClosed(), this); await requestProcessor.ProcessRequestsAsync(httpApplication); } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }HTTP1和HTTP2處理HTTP協(xié)議的方式有所不同,HTTP1協(xié)議解析完成后,會立即調(diào)用IHttpApplication<TContext>處理請求,HTTP2協(xié)議解析完成后,會再次調(diào)用ThreadPool.UnsafeQueueUserWorkItem()方法等待線程池可用線程。

結(jié)束語

Kestrel服務(wù)的代碼量并不下,其中主要是輔助接受用戶請求和解析HTTP協(xié)議的代碼,在這里不做詳細的介紹,各位讀者有興趣的,可以詳細閱讀源代碼。

我們看到,Kestrel服務(wù)在接受和處理請求時,都用到了線程池,可以極大的提高服務(wù)器的吞吐量。

后面,我們還會詳細介紹系統(tǒng)默認(rèn)的IHttpApplication<TContext>實現(xiàn),看看ASP.NET Core是如何將HTTP轉(zhuǎn)發(fā)到Controller和Action,其中又有哪些精妙的代碼呢。

敬請期待。

關(guān)鍵詞:深入

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉