asp.net core mvc剖析:KestrelServer
KestrelServer是基于Libuv開發(fā)的高性能web服務(wù)器,那我們現(xiàn)在就來看一下它是如何工作的。在上一篇文章中提到了Program的Main方法,在這個(gè)方法里Build了一個(gè)WebHost,我們?cè)賮砜匆幌麓a:
| public static void Main( string [] args) ??? { ??????? var host = new WebHostBuilder() ??????????? .UseKestrel() ??????????? .UseContentRoot(Directory.GetCurrentDirectory()) ??????????? .UseIISIntegration() ??????????? .UseStartup<Startup>() ??????????? .Build(); ?? ??????? host.Run(); ??? } |
里面有一個(gè)UseKestrel方法調(diào)用,這個(gè)方法的作用就是使用KestrelServer作為web server來提供web服務(wù)。在WebHost啟動(dòng)的時(shí)候,調(diào)用了IServer的Start方法啟動(dòng)服務(wù),由于我們使用KestrelServer作為web server,自然這里調(diào)用的就是KestrelServer.Start方法,那我們來看下KestrelServer的Start方法里主要代碼:
?首先,我們發(fā)現(xiàn)在Start方法里創(chuàng)建了一個(gè)KestrelEngine對(duì)象,具體代碼如下:
| var engine = new KestrelEngine( new ServiceContext { ??????? FrameFactory = context => ??????? { ??????????? return new Frame<TContext>(application, context); ??????? }, ??????? AppLifetime = _applicationLifetime, ??????? Log = trace, ??????? ThreadPool = new LoggingThreadPool(trace), ??????? DateHeaderValueManager = dateHeaderValueManager, ??????? ServerOptions = Options ? }); |
KestrelEngine構(gòu)造方法接受一個(gè)ServiceContext對(duì)象參數(shù),ServiceContext里包含一個(gè)FrameFactory,從名稱上很好理解,就是Frame得工廠,Frame是什么?Frame是http請(qǐng)求處理對(duì)象,每個(gè)請(qǐng)求過來后,都會(huì)交給一個(gè)Frame對(duì)象進(jìn)行受理,我們這里先記住它的作用,后面還會(huì)看到它是怎么實(shí)例化的。除了這個(gè)外,還有一個(gè)是AppLiftTime,它是一個(gè)IApplicationLifetime對(duì)象,它是整個(gè)應(yīng)用生命周期的管理對(duì)象,前面沒有說到,這里補(bǔ)充上。
| public interface IApplicationLifetime ???? { ???????? /// <summary> ???????? /// Triggered when the application host has fully started and is about to wait ???????? /// for a graceful shutdown. ???????? /// </summary> ???????? CancellationToken ApplicationStarted { get ; } ???????? /// <summary> ???????? /// Triggered when the application host is performing a graceful shutdown. ???????? /// Requests may still be in flight. Shutdown will block until this event completes. ???????? /// </summary> ???????? CancellationToken ApplicationStopping { get ; } ???????? /// <summary> ???????? /// Triggered when the application host is performing a graceful shutdown. ???????? /// All requests should be complete at this point. Shutdown will block ???????? /// until this event completes. ???????? /// </summary> ???????? CancellationToken ApplicationStopped { get ; } ???????? /// <summary> ???????? /// Requests termination the current application. ???????? /// </summary> ???????? void StopApplication(); ???? } |
IApplicationLifetime中提供了三個(gè)時(shí)間點(diǎn),
1,ApplicationStarted:應(yīng)用程序已啟動(dòng)2,ApplicationStopping:應(yīng)用程序正在停止
3,ApplicationStopped:應(yīng)用程序已停止
我們可以通過CancellationToken.Register方法注冊(cè)回調(diào)方法,在上面說到的三個(gè)時(shí)間點(diǎn),執(zhí)行我們特定的業(yè)務(wù)邏輯。IApplicationLifetime是在WebHost的Start方法里創(chuàng)建的,如果想在我們自己的應(yīng)用程序獲取這個(gè)對(duì)象,我們可以直接通過依賴注入的方式獲取即可。
?我們繼續(xù)回到ServiceContext對(duì)象,這里面還包含了Log對(duì)象,用于跟蹤日志,一般我們是用來看程序執(zhí)行的過程,并可以通過它發(fā)現(xiàn)程序執(zhí)行出現(xiàn)問題的地方。還包含一個(gè)ServerOptions,它是一個(gè)KestrelServerOptions,里面包含跟服務(wù)相關(guān)的配置參數(shù):
1,ThreadCount:服務(wù)線程數(shù),表示服務(wù)啟動(dòng)后,要開啟多少個(gè)服務(wù)線程,因?yàn)槊總€(gè)請(qǐng)求都會(huì)使用一個(gè)線程來進(jìn)行處理,多線程會(huì)提高吞吐量,但是并不一定線程數(shù)越多越好,在系統(tǒng)里默認(rèn)值是跟CPU內(nèi)核數(shù)相等。
2,ShutdownTimeout:The amount of time after the server begins shutting down before connections will be forcefully closed(在應(yīng)用程序開始停止到強(qiáng)制關(guān)閉當(dāng)前請(qǐng)求連接所等待的時(shí)間,在這個(gè)時(shí)間段內(nèi),應(yīng)用程序會(huì)等待請(qǐng)求處理完,如果還沒處理完,將強(qiáng)制關(guān)閉)
3,Limits:KestrelServerLimits對(duì)象,里面包含了服務(wù)限制參數(shù),比如MaxRequestBufferSize,MaxResponseBufferSize
其他參數(shù)就不再一個(gè)一個(gè)說明了。
KestrelEngine對(duì)象創(chuàng)建好后,通過調(diào)用?engine.Start(threadCount),根據(jù)配置的threadcount進(jìn)行服務(wù)線程KestrelThread實(shí)例化,代碼如下: public void Start(int count){ for (var index = 0; index < count; index++){Threads.Add(new KestrelThread(this));} foreach (var thread in Threads){thread.StartAsync().Wait();}}
?上面的代碼會(huì)創(chuàng)建指定數(shù)量的Thread對(duì)象,然后開始等待任務(wù)處理。KestrelThread是對(duì)libuv線程處理的封裝。
這些工作都準(zhǔn)備好后,就開始啟動(dòng)監(jiān)聽服務(wù)了,這個(gè)時(shí)候服務(wù)就開始接受http請(qǐng)求了,我們前面說到了,監(jiān)聽socket在listener類中創(chuàng)建(ListenerPrimary也是一個(gè)Listener),下面是listener的start方法
| ? public Task StartAsync( ???????????? ListenOptions listenOptions, ???????????? KestrelThread thread) ???????? { ???????????? ListenOptions = listenOptions; ???????????? Thread = thread; ???????????? var tcs = new TaskCompletionSource< int >( this ); ???????????? Thread.Post(state => ???????????? { ???????????????? var tcs2 = (TaskCompletionSource< int >) state; ???????????????? try ???????????????? { ???????????????????? var listener = ((Listener) tcs2.Task.AsyncState);??????????????????? //創(chuàng)建監(jiān)聽socket ???????????????????? listener.ListenSocket = listener.CreateListenSocket();??????????????????? //開始監(jiān)聽,當(dāng)有連接請(qǐng)求過來后,觸發(fā)ConnectionCallback方法 ???????????????????? ListenSocket.Listen(Constants.ListenBacklog, ConnectionCallback, this ); ???????????????????? tcs2.SetResult(0); ???????????????? } ???????????????? catch (Exception ex) ???????????????? { ???????????????????? tcs2.SetException(ex); ???????????????? } ???????????? }, tcs); ???????????? return tcs.Task; ???????? } </ int ></ int > |
ConnectionCallback:當(dāng)連接請(qǐng)求過來后被觸發(fā),在回調(diào)方法里,進(jìn)行連接處理分發(fā),連接分發(fā)代碼如下:
| protected virtual void DispatchConnection(UvStreamHandle socket) ??? { ??????? var connection = new Connection( this , socket); ??????? connection.Start(); ??? } |
這個(gè)是listener類中的實(shí)現(xiàn),我們前面看到,只有在線程數(shù)為1的情況下,才創(chuàng)建Listener對(duì)象進(jìn)行監(jiān)聽,否則創(chuàng)建ListenerPrimary監(jiān)聽,ListenerPrimay里重寫了方法,它的實(shí)現(xiàn)如下:
| protected override void DispatchConnection(UvStreamHandle socket) ??? {??????????? //這里采用輪詢的方式,把連接請(qǐng)求依次分發(fā)給不同的線程進(jìn)行處理 ??????? var index = _dispatchIndex++ % (_dispatchPipes.Count + 1); ??????? if (index == _dispatchPipes.Count) ??????? { ??????????? // ??????????? base .DispatchConnection(socket); ??????? } ??????? else ??????? { ??????????? DetachFromIOCP(socket); ??????????? var dispatchPipe = _dispatchPipes[index];??????????????? //這里就是通過命名pipe,傳遞socket給特定的線程 ??????????? var write = new UvWriteReq(Log); ??????????? write.Init(Thread.Loop); ??????????? write.Write2( ??????????????? dispatchPipe, ??????????????? _dummyMessage, ??????????????? socket, ??????????????? (write2, status, error, state) => ??????????????? { ??????????????????? write2.Dispose(); ??????????????????? ((UvStreamHandle)state).Dispose(); ??????????????? }, ??????????????? socket); ??????? } ??? } |
好了,連接請(qǐng)求找到處理線程后,后面就可以開始處理工作了。ListenerSecondary里的代碼比較復(fù)雜,其實(shí)最終都會(huì)調(diào)用下面的代碼完成Connection對(duì)象的創(chuàng)建?
| var connection = new Connection( this , socket); connection.Start(); |
Connection表示的就是當(dāng)前連接,下面是它的構(gòu)造方法
| public Connection(ListenerContext context, UvStreamHandle socket) : base (context) ???????? { ???????????? _socket = socket; ???????????? _connectionAdapters = context.ListenOptions.ConnectionAdapters; ???????????? socket.Connection = this ; ???????????? ConnectionControl = this ; ???????????? ConnectionId = GenerateConnectionId(Interlocked.Increment( ref _lastConnectionId)); ???????????? if (ServerOptions.Limits.MaxRequestBufferSize.HasValue) ???????????? { ???????????????? _bufferSizeControl = new BufferSizeControl(ServerOptions.Limits.MaxRequestBufferSize.Value, this ); ???????????? } ? //創(chuàng)建輸入輸出socket流 ???????????? Input = new SocketInput(Thread.Memory, ThreadPool, _bufferSizeControl); ???????????? Output = new SocketOutput(Thread, _socket, this , ConnectionId, Log, ThreadPool); ???????????? var tcpHandle = _socket as UvTcpHandle; ???????????? if (tcpHandle != null ) ???????????? { ???????????????? RemoteEndPoint = tcpHandle.GetPeerIPEndPoint(); ???????????????? LocalEndPoint = tcpHandle.GetSockIPEndPoint(); ???????????? } ? //創(chuàng)建處理frame,這里的framefactory就是前面創(chuàng)建KestrelEngine時(shí)創(chuàng)建的工廠 ???????????? _frame = FrameFactory( this ); ???????????? _lastTimestamp = Thread.Loop.Now(); ???????? } |
然后調(diào)用Connection的Start方法開始進(jìn)行處理,這里面直接把處理任務(wù)交給Frame處理,Start方法實(shí)現(xiàn):
| public void Start() ???????? { ???????????? Reset(); //啟動(dòng)了異步處理任務(wù)開始進(jìn)行處理 ???????????? _requestProcessingTask = ???????????????? Task.Factory.StartNew( ???????????????????? (o) => ((Frame)o).RequestProcessingAsync(), //具體的處理方法 ???????????????????? this , ???????????????????? default (CancellationToken), ???????????????????? TaskCreationOptions.DenyChildAttach, ???????????????????? TaskScheduler.Default).Unwrap(); ???????????? _frameStartedTcs.SetResult( null ); ???????? } |
| 1 | RequestProcessingAsync方法里不再詳細(xì)介紹了,把主要的代碼拿出來看一下: |
| 。。。。。 //_application就是上一篇文章提到的HostApplication,首先調(diào)用CreateContext創(chuàng)建HttpContext對(duì)象 var context = _application.CreateContext( this ); 。。。。。。 //進(jìn)入處理管道 await _application.ProcessRequestAsync(context).ConfigureAwait( false ); 。。。。。。 |
| ProcessRequestAsync完成處理后,把結(jié)果輸出給客戶端,好到此介紹完畢。 |
相關(guān)文章:
聊聊ASP.NET Core默認(rèn)提供的這個(gè)跨平臺(tái)的服務(wù)器——KestrelServer
原文地址:http://www.jianshu.com/p/72b13fc4ae34
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的asp.net core mvc剖析:KestrelServer的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [SSCore] 开源dotnet co
- 下一篇: .net core依赖注入的封装