.net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动

上篇讲到.net core web app是如何启动并接受请求的,下面接着探索kestrel server是如何完成此任务的。

1.kestrel server的入口KestrelServer.Start (Microsoft.AspNetCore.Hosting.Server.IHttpApplication )

FrameFactory创建的frame实例最终会交给libuv的loop回调接收请求。但是在这过程中还是有很多的初始化工作需要做的。后面我们就管中窥豹来看一看。

public void Start<TContext>(IHttpApplication<TContext> application)
{  
   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});    //启动引擎。完成libuv的配置和启动engine.Start(threadCount);  
   //针对绑定的多个地址创建server来接收请求。也就是针对ip:port来启动tcp监听foreach (var address in _serverAddresses.Addresses.ToArray()){engine.CreateServer(ipv4Address);} }

2.启动kestrel engine。engine.Start(threadCount);

启动绑定的端口*最大处理线程的thread。并初始化libuv组件。
每一个线程初始化libuv,注册loop回调等,并启动libuv。

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();} }

private
void ThreadStart(object parameter)
{
   lock (_startSync){    
       var tcs = (TaskCompletionSource<int>) parameter;    
        try{            //初始化loop_loop.Init(_engine.Libuv);        
            //注册loop回调//EnqueueCloseHandle:持有的资源释放后的回调方法,回调往queue内增加一个item,事件循环该queue完成资源的最终释放_post.Init(_loop, OnPost, EnqueueCloseHandle);    
                //注册心跳定时器_heartbeatTimer.Init(_loop, EnqueueCloseHandle);      
            //启动心跳定时器_heartbeatTimer.Start(OnHeartbeat, timeout: HeartbeatMilliseconds, repeat: HeartbeatMilliseconds);_initCompleted = true;tcs.SetResult(0);}      
        catch (Exception ex){tcs.SetException(ex);    
               return;}}  
    try{      
         //当前线程执行到Run()这里会挂起_loop.Run();      
      //应用程序stop,shutdown之类的情况,libuv唤醒当前线程,完成资源清理if (_stopImmediate){            // thread-abort form of exit, resources will be leaked//线程中止形式的退出,资源会被泄露。return;}        // run the loop one more time to delete the open handles//再次运行循环以删除打开的句柄_post.Reference();_post.Dispose();_heartbeatTimer.Dispose();        // Ensure the Dispose operations complete in the event loop.//确保事件循环中的Dispose操作完成。_loop.Run();_loop.Dispose();}  
    catch (Exception ex){_closeError = ExceptionDispatchInfo.Capture(ex);      
      // Request shutdown so we can rethrow this exception// in Stop which should be observable.//请求关闭,以便我们可以重新抛出此异常在停止应该是可观察的。_appLifetime.StopApplication();}  
    finally{_threadTcs.SetResult(null);} }

3.libuv启动完成之后,接着就是处理订阅注册tcp了。

回到1的kestrel的start中。接着执行engine.CreateServer(ipv4Address);,这里和.net 里面的tcplistener不太一样。.net里面就是listener bind,start,accept就好了。而libuv涉及到一个多路io复用的概念,这也是为什么使用他能高并发的原因。

public IDisposable CreateServer(ServerAddress address){ 
   var usingPipes = address.IsUnixPipe;  
    var pipeName = (Libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n");  
    var single = Threads.Count == 1;  
     var first = true;  
    foreach (var thread in Threads){        if(single){}//single就不考虑,这种情况真是环境是不会这样玩的else if (first)      
       
{            //根据当前平台创建tcp listenervar listener = usingPipes? (ListenerPrimary)new PipeListenerPrimary(ServiceContext): new TcpListenerPrimary(ServiceContext);listener.StartAsync(pipeName, address, thread).Wait();}    
       else{            //如果是多次对同一个ip:port做监听var listener = usingPipes? (ListenerSecondary)new PipeListenerSecondary(ServiceContext): new TcpListenerSecondary(ServiceContext);listener.StartAsync(pipeName, address, thread).Wait();}first = false;} }

tcplistener启动细节,这里就只看TcpListenerPrimary了。

首先说明一下TcpListenerPrimary这个类的继承关系:TcpListenerPrimary -->ListenerPrimary -->Listener。这样才有助于后续代码的理解。
后续代码到处都能看到thread.post/postaysnc的代码。这玩意的意思是把传入的action放到libuv loop中,并激活异步完成回调。libuv另一个重要的概念各种回调。
1.接着上面的代码,我们进入TcpListenerPrimary.StartAsync()方法。方法在ListenerPrimary中。

public async Task StartAsync(string pipeName, ServerAddress address, KestrelThread thread){_pipeName = pipeName;  
   await StartAsync(address, thread).ConfigureAwait(false);  
   await Thread.PostAsync(state => ((ListenerPrimary)state).PostCallback(), this).ConfigureAwait(false); }

2.接着上面的代码进入StartAsync(address, thread)。他是父类Listener的方法。

public Task StartAsync(ServerAddress address, KestrelThread thread){ServerAddress = address; Thread = thread;  
  var tcs = new TaskCompletionSource<int>(this);Thread.Post(state =>{        var tcs2 = (TaskCompletionSource<int>)state;      
        var listener = ((Listener)tcs2.Task.AsyncState);        //创建socketlistener.ListenSocket = listener.CreateListenSocket();        ////socket监听,libu注册监听并设置回调函数,最大队列。ListenSocket.Listen(Constants.ListenBacklog, ConnectionCallback, this);tcs2.SetResult(0);}, tcs);  
    return tcs.Task; }

protected
override UvStreamHandle CreateListenSocket()
{    //初始化socket并bind到addressvar socket = new UvTcpHandle(Log);socket.Init(Thread.Loop, Thread.QueueCloseHandle);    //是否使用Nagle's algorithm算法。socket.NoDelay(ServerOptions.NoDelay);socket.Bind(ServerAddress);  
 // If requested port was "0", replace with assigned dynamic port.ServerAddress.Port = socket.GetSockIPEndPoint().Port;  
   return socket; }

在接着上面的代码ListenSocket.Listen成功之后,libuv回调ConnectionCallback函数。

进入ConnectionCallback函数,完成重要的listen Accept.

step1:listen成功libuv回调ConnectionCallback方法。
step2:初始化接收请求socket,并将之关联到监听socket
step3:适配接收请求socket,如果是第一次适配的话则创建connection
step4:创建connection并启动
step5:new connection 关联 Frame 对象。
step6:启动frame
step7:由Connection类调用一次以开始RequestProcessingAsync循环。
step8:循环接收请求,接收请求到之后交给上层程序处理

private static void ConnectionCallback(UvStreamHandle stream, int status, Exception error, object state){    
   var listener = (Listener)state;listener.OnConnection(stream, status);//step 1}

protected override void OnConnection(UvStreamHandle listenSocket, int status)//step 2{    var acceptSocket = new UvTcpHandle(Log);acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);acceptSocket.NoDelay(ServerOptions.NoDelay);listenSocket.Accept(acceptSocket);DispatchConnection(acceptSocket); }

protected override void DispatchConnection(UvStreamHandle socket)// step 3
{  
   var index = _dispatchIndex++ % (_dispatchPipes.Count + 1);  
   if (index == _dispatchPipes.Count){    
      base.DispatchConnection(socket);}    
   else{DetachFromIOCP(socket);  
       var dispatchPipe = _dispatchPipes[index];    
       var write = new UvWriteReq(Log);write.Init(Thread.Loop);write.Write2(dispatchPipe, _dummyMessage, socket,(write2, status, error, state) =>{write2.Dispose();((UvStreamHandle)state).Dispose();},socket);} }
    
protected virtual void DispatchConnection(UvStreamHandle socket)//step 4
{  
  var connection = new Connection(this, socket);connection.Start(); }

private
Func<ConnectionContext, Frame> FrameFactory => ListenerContext.ServiceContext.FrameFactory;public Connection(ListenerContext context, UvStreamHandle socket) : base(context)//step 5
{SocketInput = new SocketInput(Thread.Memory, ThreadPool, _bufferSizeControl);SocketOutput = new SocketOutput(Thread, _socket, this, ConnectionId, Log, ThreadPool);  
 //重点代码在这里,FrameFactory是一个委托,是KestrelServer.Start中注册的action_frame = FrameFactory(this); }
 
public void Start()//step 6
{Log.ConnectionStart(ConnectionId);    
 // Start socket prior to applying the ConnectionFilter_socket.ReadStart(_allocCallback, _readCallback, this);_frame.Start(); }

/// <summary>
/// Called once by Connection class to begin the RequestProcessingAsync loop.
/// </summary>
public void Start()//step 7
{Reset();_requestProcessingTask =Task.Factory.StartNew((o) => ((Frame)o).RequestProcessingAsync(),            this,            default(CancellationToken),TaskCreationOptions.DenyChildAttach,TaskScheduler.Default).Unwrap(); }

/// <summary>
/// 主循环消耗套接字输入,将其解析为协议帧,并调用应用程序委托,只要套接字打算保持打开。
/// 从此循环得到的任务将保留在服务器需要时使用的字段中以排除和关闭所有当前活动的连接。/// </summary>
public override async Task RequestProcessingAsync(){
   while (!_requestProcessingStopping){InitializeHeaders();      
     var context = _application.CreateContext(this);  
      await application.ProcessRequestAsync(context).ConfigureAwait(false);} }

相关文章:

  • .net core 源码解析-web app是如何启动并接收处理请求

原文地址:http://www.cnblogs.com/calvinK/p/6126020.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/327307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL中的any_value()函数

https://blog.csdn.net/u014079773/article/details/93722761 https://www.thinbug.com/q/37089347 https://blog.csdn.net/Peacock__/article/details/90608246 https://www.itranslater.com/qa/details/2109775246877262848 4.any_value()会选择被分到同一组的数据里…

MybatisPlus使用

Mybatisplus 导入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</gro…

.net core 源码解析-mvc route的注册,激活,调用流程(三)

.net core mvc route的注册&#xff0c;激活&#xff0c;调用流程 mvc的入口是route&#xff0c;当前请求的url匹配到合适的route之后&#xff0c;mvc根据route所指定的controller和action激活controller并调用action完成mvc的处理流程。下面我们看看服务器是如何调用route的。…

高可用性的几个级别

转载自 高可用性的几个级别 大家常说高可用&#xff0c;High Availablility&#xff0c;但是一般说到这个词的时候&#xff0c;具体指的什么方案呢&#xff1f; 级别一&#xff1a;FT (Fault Tolerance) 双击热备 通过创建与主实例保持虚拟同步的虚拟机&#xff0c;使应用在服…

mysql - Docker Wordpress连接到本地主机上的数据库服务器

视频上面的 docker service create --name mysql -p 3306:3306 --env MYSQL_ROOT_PASSWORDroot \ --env MYSQL_DATABASEwordpress \ --network demo \ --mount typevolume,sourcemysql-data,destination/var/lib/mysql \ mysql:5.7 docker service create -…

CoreCRM 开发实录——开始之新项目的技术选择

2016年11月&#xff0c;接受了一个工作&#xff0c;是对“悟空CRM”进行一些修补。这是一个不错的 CRM&#xff0c;开源&#xff0c;并提供一个 SaaS 的服务。正好微软的 .NET Core 和 ASP.NET Core 也发布了。于是就有了这个想法&#xff1a;使用 ASP.NET Core 来开发一个 CRM…

80%的程序员都不了解的调试技巧

转载自 80%的程序员都不了解的调试技巧 程序员的工作内容&#xff0c;除了大部分时间写代码之外&#xff0c;因为有不少的时间是用在调试代码上。甚至说不是在调试代码&#xff0c;就是即将调试代码。 :) 今天我们来谈谈调试代码的一些技巧&#xff0c;在使用IDE提供的debu…

复制vmware overLay网络无法ping通 ping www.baidu.com可以

因为忘记关闭防火墙了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 要永久关闭&#xff01;&#xff01; 修改hostname https://blog.csdn.net/qq_27327261/article/details/109100219 关闭防火墙 https://blog.csdn.net/qq_27327261/article/details/1…

2016.NET Core相关内容回顾

每一年的脚步的确是快&#xff0c;转眼间马上就2017。.NET Core 2014年宣布开源以来&#xff0c;在2016年发布了第一个版本&#xff0c;2017年将发布第二个版本&#xff0c;在这新年之际&#xff0c;我们回顾2016年&#xff0c;新的一年&#xff0c;带着理想和抱负继续出发。 1…

微服务化的数据库设计与读写分离

转载自 微服务化的数据库设计与读写分离 数据库永远是应用最关键的一环&#xff0c;同时越到高并发阶段&#xff0c;数据库往往成为瓶颈&#xff0c;如果数据库表和索引不在一开始就进行良好的设计&#xff0c;则后期数据库横向扩展&#xff0c;分库分表都会遇到困难。 对于…

centos7 切换中文输入法 无需安装

*************** 当你发现自己的才华撑不起野心时&#xff0c;就请安静下来学习吧&#xff01;***************

Consul 服务注册与服务发现

1. 服务注册 对 Consul 进行服务注册之前&#xff0c;需要先部署一个服务站点&#xff0c;我们可以使用 ASP.NET Core 创建 Web 应用程序&#xff0c;并且部署到 Ubuntu 服务器上。 ASP.NET Core Hell World 应用程序示例代码&#xff0c;只需要三个文件&#xff0c;Startup.cs…

tar (child): .tgz\r:无法 open: 没有那个文件或目录

Linux下运行bash脚本显示“: /usr/bin/env: "bash\r": 没有那个文件或目录 程序员小熊 2017-12-18 14:45:45 18395 收藏 7 分类专栏&#xff1a; Linux 版权 用 ./ 运行bash脚本文件出现 报错信息 /usr/bin/env: "bash\r": 没有那个文件或目录 错误原…

Entity Framework Core 1.1 升级通告

Entity Framework Core&#xff08;EF Core&#xff09;是一个轻量级的&#xff0c;可扩展和实体框架的跨平台版本。今天&#xff0c;我们宣布 Entity Framewor Core 1.1 正式可用了。 EF Core 和 .NET Core 遵循相同的发行周期。每2个月不断的改进和每6个月的新功能发布。这是…

聊聊微服务架构及分布式事务解决方案

转载自 聊聊微服务架构及分布式事务解决方案 分布式事务场景如何设计系统架构及解决数据一致性问题&#xff0c;个人理解最终方案把握以下原则就可以了&#xff0c;那就是&#xff1a;大事务小事务&#xff08;原子事务&#xff09;异步&#xff08;消息通知&#xff09;&am…

使用熔断器设计模式保护软件

作为软件开发人员&#xff0c;我们的生活是快节奏的&#xff0c;我们采用的是敏捷软件开发方法&#xff0c;迭代式的开发我们软件功能&#xff0c;开发完成提交测试&#xff0c;通过了QA的测试后被部署到生产环境&#xff0c;然后可怕的事情在生产环境里发生了&#xff0c;生产…

hql投影查询之—— [Ljava.lang.Object; cannot be cast to cn.bdqn.guanMingSys.entity.Notice

最近在做项目中遇到一个简单的问题&#xff0c;但是解决了好久&#xff0c;问题就是投影查询。 先来看看出现的异常&#xff1a; java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to cn.bdqn.guanMingSys.entity.Notice at cn.bdqn.guanMingSys.dao.i…

架构师之路:从码农到架构师你差了哪些

转载自 架构师之路&#xff1a;从码农到架构师你差了哪些 Web应用&#xff0c;最常见的研发语言是Java和PHP。 后端服务&#xff0c;最常见的研发语言是Java和C/C。 大数据&#xff0c;最常见的研发语言是Java和Python。 可以说&#xff0c;Java是现阶段中国互联网公司中&…

Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制

将通用的序列号生成器库 从SQL Server迁移到Mysql 遇到的一个问题&#xff0c;就是TimeStamp/RowVersion并发控制类型在非Microsoft SQL Server数据库中的实现。SQL Server timestamp 数据类型与时间和日期无关。SQL Server timestamp 是二进制数字&#xff0c;它表明数据库中数…

干货 | Tomcat 连接数与线程池详解

转载自 干货 | Tomcat 连接数与线程池详解 前言 在使用tomcat时&#xff0c;经常会遇到连接数、线程数之类的配置问题&#xff0c;要真正理解这些概念&#xff0c;必须先了解Tomcat的连接器&#xff08;Connector&#xff09;。 在前面的文章 详解Tomcat配置文件server.xml…