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

最近.net core 1.1也发布了,蹒跚学步的小孩又长高了一些,园子里大家也都非常积极的在学习,闲来无事,扒拔源码,涨涨见识。

先来见识一下web站点是如何启动的,如何接受请求,.net core web app最简单的例子,大约长这样

 public static void Main(string[] args)    
{          
 //dotnet NetCoreWebApp.dll --server.urls="http://localhost:5000/;http://localhost:5001/"var config = new ConfigurationBuilder().AddCommandLine(args).Build();       new WebHostBuilder().UseConfiguration(config).UseKestrel().UseContentRoot(Directory.GetCurrentDirectory())                //.UseIISIntegration().UseStartup<Startup>()                //.Configure(confApp =>//{//    confApp.Run(context =>//    {//        return context.Response.WriteAsync("hello");//    });//}).Build().Run();}

WebHostBuilder看名字也知道是为了构建WebHost而存在的。在构建WebHost的路上他都做了这些:如加载配置,注册服务,配置功能等。

1.1 加载配置

builder内部维护了一个IConfiguration _config,可以简单的理解为key-value集合对象。可以通过UseSetting增加,也可以通过UseConfiguration增加

WebHostBuilder对UseStartup ()的解析实现

我们从官方代码例子中能看到Startup类只是一个普通的类,builder是如何调用到这个类的方法的呢?
Build方法关于这一块的代码大概如下:

private IServiceCollection BuildHostingServices(){   

 var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);
    if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())){services.AddSingleton(typeof(IStartup), startupType);}  
   else{services.AddSingleton(typeof(IStartup), sp =>{        
     var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();          
      var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);          
       return new ConventionBasedStartup(methods);});} }

能看出来其实Startup可以是一个实现了IStartup接口的类。为什么官方还需要搞一个普通类的方式呢?其实这里还有一个小技巧:
针对Configure和ConfigureServices方法我们还可以做的更多,那就是根据不同的environmentName调用不同的方法。
Configure方法可以是Configure+EnvironmentName,ConfigureServices则是Configure+EnvironmentName+Services。这样的话还能做到区分环境进去不同的配置。
下面代码展示了builder是如何选择这2个方法的

 private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName)       
 
{          

 var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);  
 return new ConfigureBuilder(configureMethod);}      
 
private static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName)    
{            
 var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);      
       return servicesMethod == null ? null : new ConfigureServicesBuilder(servicesMethod); }

1.2 Build()

根据之前use的各类配置,服务,参数等构建WebHost

public IWebHost Build(){    // Warn about deprecated environment variablesif (Environment.GetEnvironmentVariable("Hosting:Environment") != null){Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");}   
 if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null){Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");}  
  if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null){Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");}
   var hostingServices = BuildHostingServices();  
   var hostingContainer = hostingServices.BuildServiceProvider();  
   var host = new WebHost(hostingServices, hostingContainer, _options, _config);host.Initialize();  
   return host; }

2.1 构建WebHost

调用Initialize完成,host的初始化工作。Initialize 调用一次BuildApplication();

public void Initialize(){   
 if (_application == null){_application = BuildApplication();} }
 
 private RequestDelegate BuildApplication(){  
  //获取ServiceCollection中的IStartup,完成我们Startup.ConfigureService方法的调用,将我们代码注册的service加入到系统EnsureApplicationServices();  
   //解析可以为urls或server.urls的value为绑定的address。以;分割的多个地址//初始化UseKestrel(),UseIISIntegration()等指定的 实现了IServer接口的serverEnsureServer();  
    var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();    var builder = builderFactory.CreateBuilder(Server.Features);builder.ApplicationServices = _applicationServices;
   var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();Action<IApplicationBuilder> configure = _startup.Configure;  
   foreach (var filter in startupFilters.Reverse()){configure = filter.Configure(configure);}configure(builder);  
   return builder.Build(); }

2.2 ApplicationBuilderFactory.Build();

根据Server.Features build ApplicationBuilderFactory对象。 完成ApplicationBuilderFactory的build过程。
大致就是注册各类中间件_components(middleware),也就是说的这个 https://docs.asp.net/en/latest/fundamentals/middleware.html
借用官方的图说明一下什么是middleware。

public RequestDelegate Build()      
 
{RequestDelegate app = context =>{context.Response.StatusCode = 404;                return TaskCache.CompletedTask;};        
   foreach (var component in _components.Reverse()){app = component(app);}          
     return app;}

2.3 builder完成之后,接着执行Run方法启动web服务

启动host。host.Run();最终调用到WebHost.Start(),并调用当前app指定的Server对象启动web服务

public virtual void Start()      
 
{Initialize();_logger = _applicationServices.GetRequiredService<ILogger<WebHost>>();            var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticSource>();            var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();_logger.Starting();Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));_applicationLifetime.NotifyStarted();_logger.Started();}

2.4 KestrelHttpServer的Start方法,启动对监听的监听接收请求

简化代码大约这样子

public void Start<TContext>(IHttpApplication<TContext> application)
{    
   var engine = new KestrelEngine(new ServiceContext{                            //接收到请求之后,回调FrameFactory方法,开始处理请求FrameFactory = context =>{                            return new Frame<TContext>(application, context);},                            //启动完成,停止等通知事件AppLifetime = _applicationLifetime,Log = trace,ThreadPool = new LoggingThreadPool(trace),DateHeaderValueManager = dateHeaderValueManager,ServerOptions = Options});    //启动工作线程engine.Start(threadCount);  
    foreach (var address in _serverAddresses.Addresses.ToArray()){        //判断ipv4,ipv6,localhosts得到监听的地址,并启动对该端口的监听,等待请求进来engine.CreateServer(address)} }//engine.Start(threadCount);


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

engine.CreateServer(address)
先不说了,是tcpListener的一堆代码。看了代码感觉这里又是深不可测,先放着,有空了在撸这一部分。需要理解tcpListener为何如此设计,需要精读这部分代码

2.5 接收请求后的处理

listerner接到请求之后 实例化Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Connection,并调用该对象的Start()
接着由Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame .Start() 异步启动task开始处理请求。

KestrelHttpServer处理请求:Frame .RequestProcessingAsync();

public override async Task RequestProcessingAsync(){    
var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);_keepAlive = messageBody.RequestKeepAlive;_upgrade = messageBody.RequestUpgrade;InitializeStreams(messageBody);  
 var context = _application.CreateContext(this);  
    await _application.ProcessRequestAsync(context).ConfigureAwait(false);    //经过一系列的检查,各种判断,请求终于由KestrelHttpServer交给了统一的HostVerifyResponseContentLength(); }

这里的application 就是Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));这里实例化的HostingApplication
也就是Microsoft.AspNetCore.Hosting.Internal下面的public class HostingApplication : IHttpApplication

2.6 httpcontext的创建 _application.CreateContext(this);

public Context CreateContext(IFeatureCollection contextFeatures)        {    
 var httpContext = _httpContextFactory.Create(contextFeatures);    
 
  var diagnoticsEnabled =
_diagnosticSource.IsEnabled("Microsoft.AspNetCore.Hosting.BeginRequest");  
 var startTimestamp = (diagnoticsEnabled || _logger.IsEnabled(LogLevel.Information)) ? Stopwatch.GetTimestamp() : 0;            var scope = _logger.RequestScope(httpContext);_logger.RequestStarting(httpContext);      
       if (diagnoticsEnabled){_diagnosticSource.Write("Microsoft.AspNetCore.Hosting.BeginRequest", new { httpContext = httpContext, timestamp = startTimestamp });}          
        return new Context{HttpContext = httpContext,Scope = scope,StartTimestamp = startTimestamp,};}

2.7 Host处理请求

```C#
public Task ProcessRequestAsync(Context context)
{
return _application(context.HttpContext);
}

~~~
这里的_application就是Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));中的_application,也就是BuildApplication()构建出来的RequestDelegate。开启mvc处理流程

3 mvc接受请求,开始处理流程


mvc大致调用顺序:Startup.Configure方法中


//1app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});//2public static IApplicationBuilder UseMvc(this IApplicationBuilder app, Action<IRouteBuilder> onfigureRoutes)      
 
{            return app.UseRouter(routes.Build());}//3

public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router){  
 if (builder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null){      
  throw new InvalidOperationException(Resources.FormatUnableToFindServices(nameof(IServiceCollection),nameof(RoutingServiceCollectionExtensions.AddRouting),            "ConfigureServices(...)"));}    //注册一个Middleware接收请求,开始处理.如2.2所展示的代码,RouterMiddleware将加入到_components,由2.7完成调用return builder.UseMiddleware<RouterMiddleware>(router); }

至此,mvc框架才真正开始处理我们的web请求。host的配置,启动,监听,接受请求,转交给上层服务的大概脉络逻辑就说完了。

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


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

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

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

相关文章

关于文档的基本操作---ElasticSearch

关于文档的基本操作&#xff08;重点&#xff09; 基本操作 添加数据 PUT /psz/user/1 {"name": "psz","age": 22,"desc": "偶像派程序员","tags": ["暖","帅"] }获取数据 GEt psz/user/…

IdentityServer4 使用OpenID Connect添加用户身份验证

使用IdentityServer4 实现OpenID Connect服务端&#xff0c;添加用户身份验证。客户端调用&#xff0c;实现授权。 IdentityServer4 目前已更新至1.0 版&#xff0c;在之前的文章中有所介绍。IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API 。 本文环…

深度优先搜索和广度优先搜索

转载自 深度优先搜索和广度优先搜索 图的应用很广泛&#xff0c;也有很多非常有用的算法&#xff0c;当然也有很多待解决的问题&#xff0c;根据性质&#xff0c;图可以分为无向图和有向图。 图 之所以要研究图&#xff0c;是因为图在生活中应用比较广泛&#xff1a; 图是若…

消息队列 Kafka 的基本知识及 .NET Core 客户端

前言 最新项目中要用到消息队列来做消息的传输&#xff0c;之所以选着 Kafka 是因为要配合其他 java 项目中&#xff0c;所以就对 Kafka 了解了一下&#xff0c;也算是做个笔记吧。 本篇不谈论 Kafka 和其他的一些消息队列的区别&#xff0c;包括性能及其使用方式。 简介 Kafka…

深入解读Service Mesh背后的技术细节

转载自 深入解读Service Mesh背后的技术细节 在Kubernetes称为容器编排的标准之后&#xff0c;Service Mesh开始火了起来&#xff0c;但是很多文章讲概念的多&#xff0c;讲技术细节的少&#xff0c;所以专门写一篇文章&#xff0c;来解析Service Mesh背后的技术细节。 一、…

CentOS7查看和关闭防火墙

https://blog.csdn.net/ytangdigl/article/details/79796961 CentOS7查看和关闭防火墙 蔚蓝色天空sky 2018-04-02 23:22:21 708762 收藏 236 分类专栏&#xff1a; linux 文章标签&#xff1a; centos 防火墙 CentOS 7.0默认使用的是firewall作为防火墙 查看防火墙状态 f…

Visual Studio Code 1.8版本添加了Hot Exit、Zen Mode及更多调试选项

最新发布的Visual Studio Code 1.8版本有许多改进和新功能&#xff0c;包括防止丢失任何编辑信息的Hot Exit&#xff0c;方便开发人员把注意力集中在代码上的Zen Mode&#xff0c;新的调试功能以及更方便的设置等。 Hot Exit是一项新功能&#xff0c;目的是在应用程序崩溃或退出…

ElasticSearch(笔记)

简介 本教程基于ElasticSearch7.6.1, 注意ES7的语法与ES6的API调用差别很大, 教程发布时最新版本为ES7.6.2(20200401更新);ES是用于全文搜索的工具: SQL: 使用like %关键词%来进行模糊搜索在大数据情况下是非常慢的, 即便设置索引提升也有限;ElasticSearch: 搜索引擎(baidu, …

漫画:什么是冒泡排序

转载自 漫画&#xff1a;什么是冒泡排序 什么是冒泡排序&#xff1f; 冒泡排序的英文Bubble Sort&#xff0c;是一种最基础的交换排序。 大家一定都喝过汽水&#xff0c;汽水中常常有许多小小的气泡&#xff0c;哗啦哗啦飘到上面来。这是因为组成小气泡的二氧化碳比水要轻…

CentOS - 修改主机名教程(将 localhost.localdomain 改成其它名字)

https://www.cnblogs.com/gudi/p/7846978.html 需要关闭防火墙&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; Linux修改主机名称 碰到这个问题的时候&#xff0c;是在安装Zookeeper集群的时候&#xff0c;碰到如下问题 java.net.U…

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

上篇讲到.net core web app是如何启动并接受请求的&#xff0c;下面接着探索kestrel server是如何完成此任务的。 1.kestrel server的入口KestrelServer.Start (Microsoft.AspNetCore.Hosting.Server.IHttpApplication ) FrameFactory创建的frame实例最终会交给libuv的loop回调…

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…