.net core 源码解析-web app是如何启动并接收处理请求
2016-12-29 07:04
519 查看
最近.net core 1.1也发布了,蹒跚学步的小孩又长高了一些,园子里大家也都非常积极的在学习,闲来无事,扒拔源码,涨涨见识。
先来见识一下web站点是如何启动的,如何接受请求,.net core web app最简单的例子,大约长这样
WebHostBuilder看名字也知道是为了构建WebHost而存在的。在构建WebHost的路上他都做了这些:如加载配置,注册服务,配置功能等。
WebHostBuilder对UseStartup
我们从官方代码例子中能看到Startup类只是一个普通的类,builder是如何调用到这个类的方法的呢?
Build方法关于这一块的代码大概如下:
能看出来其实Startup可以是一个实现了IStartup接口的类。为什么官方还需要搞一个普通类的方式呢?其实这里还有一个小技巧:
针对Configure和ConfigureServices方法我们还可以做的更多,那就是根据不同的environmentName调用不同的方法。
Configure方法可以是Configure+EnvironmentName,ConfigureServices则是Configure+EnvironmentName+Services。这样的话还能做到区分环境进去不同的配置。
下面代码展示了builder是如何选择这2个方法的
大致就是注册各类中间件_components(middleware),也就是说的这个 https://docs.asp.net/en/latest/fundamentals/middleware.html
借用官方的图说明一下什么是middleware。
engine.CreateServer(address)
先不说了,是tcpListener的一堆代码。看了代码感觉这里又是深不可测,先放着,有空了在撸这一部分。需要理解tcpListener为何如此设计,需要精读这部分代码
接着由Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame
.Start() 异步启动task开始处理请求。
KestrelHttpServer处理请求:Frame
这里的application 就是Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));这里实例化的HostingApplication
也就是Microsoft.AspNetCore.Hosting.Internal下面的public class HostingApplication : IHttpApplication
public Task ProcessRequestAsync(Context context)
{
return _application(context.HttpContext);
}
~~~
这里的_application就是Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory));中的_application,也就是BuildApplication()构建出来的RequestDelegate。开启mvc处理流程
至此,mvc框架才真正开始处理我们的web请求。host的配置,启动,监听,接受请求,转交给上层服务的大概脉络逻辑就说完了。
原文地址:http://www.cnblogs.com/calvinK/p/6008915.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
先来见识一下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的各类配置,服务,参数等构建WebHostpublic IWebHost Build(){ // Warn about deprecated environment variables if (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接口的server EnsureServer(); 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交给了统一的Host VerifyResponseContentLength(); }
这里的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跨平台或扫描二维码关注
相关文章推荐
- .net core 源码解析-web app是如何启动并接收处理请求
- .net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动
- .net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动
- android smack源码分析——接收消息以及如何解析消息
- Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程
- Hadoop源码解析之: TextInputFormat如何处理跨split的行
- tomcat源码解析(三)--请求过程之数据的接收
- android smack源码分析——接收消息以及如何解析消息【3】
- Tomcat请求处理过程(Tomcat源码解析五)
- Hadoop源码解析之: TextInputFormat如何处理跨split的行
- Netty 4源码解析:请求处理
- 学习ASP.NET Core, 怎能不了解请求处理管道[3]: 自定义一个服务器感受一下管道是如何监听、接收和响应请求的
- Hadoop源码解析之: TextInputFormat如何处理跨split的行
- JFinal源码解析--从请求到处理返回流程
- Tomcat是如何启动及运行—对tomcat的源码解析
- android smack源码分析——接收消息以及如何解析消息
- Hadoop源码解析之: TextInputFormat如何处理跨split的行
- android smack源码分析——接收消息以及如何解析消息
- 关于java 发送http json数据格式请求时,服务器端如何接收json数据并解析
- Hadoop源码解析之: TextInputFormat如何处理跨split的行