• ASP.NET Core管道详解[5]: ASP.NET Core应用是如何启动的?[上篇]


    我们知道ASP.NET Core应用的请求处理管道是由一个IServer对象和IHttpApplication对象构成的。我们可以根据需要注册不同类型的服务器,但在默认情况下,IHttpApplication是一个HostingApplication对象。一个HostingApplication对象由指定的RequestDelegate对象来完成所有的请求处理工作,而后者代表所有中间件按照注册的顺序串联而成的委托链。所有的这一切都被GenericWebHostService整合在一起,在对这个承载Web应用的服务做进一步介绍之前,下面先介绍与它相关的配置选项。[本文节选自《ASP.NET Core 3框架揭秘》第13章, 更多关于ASP.NET Core的文章请点这里]

    目录
    一、配置选项:GenericWebHostServiceOptions
    二、承载服务:GenericWebHostService
    三、应用启动流程
    四、关闭应用

    一、配置选项:GenericWebHostServiceOptions

    GenericWebHostService这个承载服务的配置选项类型为GenericWebHostServiceOptions。如下面的代码片段所示,这个内部类型有3个属性,其核心配置选项由WebHostOptions属性承载。GenericWebHostServiceOptions类型的ConfigureApplication属性返回的Action<IApplicationBuilder>对象用来注册中间件,启动过程中针对中间件的注册最终都会转移到这个属性上。

    internal class GenericWebHostServiceOptions
    {
        public WebHostOptions WebHostOptions { get; set; }
        public Action<IApplicationBuilder> ConfigureApplication { get; set; }
        public AggregateException HostingStartupExceptions { get; set; }
    }

    如何放置你的初始化代码》提出,可以利用一个外部程序集中定义的IHostingStartup实现类型来完成初始化任务,而GenericWebHostServiceOptions类型的HostingStartupExceptions属性返回的AggregateException对象就是对这些初始化任务执行过程中抛出异常的封装。一个WebHostOptions对象承载了与IWebHost相关的配置选项,虽然在基于IHost/IHostBuilder的承载系统中,IWebHost接口作为宿主的作用已经不存在,但是WebHostOptions这个配置选项依然被保留下来。

    public class WebHostOptions
    {
        public string ApplicationName { get; set; }
        public string Environment { get; set; }
        public string ContentRootPath { get; set; }
        public string WebRoot { get; set; }
        public string StartupAssembly { get; set; }
        public bool PreventHostingStartup { get; set; }
        public IReadOnlyList<string> HostingStartupAssemblies { get; set; }
        public IReadOnlyList<string> HostingStartupExcludeAssemblies { get; set; }
        public bool CaptureStartupErrors { get; set; }
        public bool DetailedErrors { get; set; }
        public TimeSpan ShutdownTimeout { get; set; }
    
        public WebHostOptions() => ShutdownTimeout = TimeSpan.FromSeconds(5.0);
        public WebHostOptions(IConfiguration configuration);
        public WebHostOptions(IConfiguration configuration, string applicationNameFallback);
    }

    一个WebHostOptions对象可以根据一个IConfiguration对象来创建,当我们调用这个构造函数时,它会根据预定义的配置键从该IConfiguration对象中提取相应的值来初始化对应的属性。

    public static class WebHostDefaults
    {
        public static readonly string ApplicationKey = "applicationName";
        public static readonly string StartupAssemblyKey = "startupAssembly";
        public static readonly string DetailedErrorsKey = "detailedErrors";
        public static readonly string EnvironmentKey = "environment";
        public static readonly string WebRootKey = "webroot";
        public static readonly string CaptureStartupErrorsKey = "captureStartupErrors";
        public static readonly string ServerUrlsKey = "urls";
        public static readonly string ContentRootKey = "contentRoot";
        public static readonly string PreferHostingUrlsKey = "preferHostingUrls";
        public static readonly string PreventHostingStartupKey = "preventHostingStartup";
        public static readonly string ShutdownTimeoutKey = "shutdownTimeoutSeconds";
    
        public static readonly string HostingStartupAssembliesKey= "hostingStartupAssemblies";
        public static readonly string HostingStartupExcludeAssembliesKey= "hostingStartupExcludeAssemblies";
    }

    二、承载服务:GenericWebHostService

    从如下所示的代码片段可以看出,GenericWebHostService的构造函数中会注入一系列的依赖服务或者对象,其中包括用来提供配置选项的IOptions<GenericWebHostServiceOptions>对象、作为管道“龙头”的服务器、用来创建ILogger对象的ILoggerFactory对象、用来发送相应诊断事件的DiagnosticListener对象、用来创建HttpContext上下文的IHttpContextFactory对象、用来创建IApplicationBuilder对象的IApplicationBuilderFactory对象、注册的所有IStartupFilter对象、承载当前应用配置的IConfiguration对象和代表当前承载环境的IWebHostEnvironment对象。在GenericWebHostService构造函数中注入的对象或者由它们创建的对象(如由ILoggerFactory对象创建的ILogger对象)最终会存储在对应的属性上。

    internal class GenericWebHostService : IHostedService
    {
        public GenericWebHostServiceOptions Options { get; }
        public IServer Server { get; }
        public ILogger Logger { get; }
        public ILogger LifetimeLogger { get; }
        public DiagnosticListener DiagnosticListener { get; }
        public IHttpContextFactory HttpContextFactory { get; }
        public IApplicationBuilderFactory ApplicationBuilderFactory { get; }
        public IEnumerable<IStartupFilter> StartupFilters { get; }
        public IConfiguration Configuration { get; }
        public IWebHostEnvironment HostingEnvironment { get; }
    
        public GenericWebHostService(IOptions<GenericWebHostServiceOptions> options,
            IServer server, ILoggerFactory loggerFactory,
            DiagnosticListener diagnosticListener, IHttpContextFactory httpContextFactory,
            IApplicationBuilderFactory applicationBuilderFactory,
            IEnumerable<IStartupFilter> startupFilters, IConfiguration configuration,
            IWebHostEnvironment hostingEnvironment);
    
        public Task StartAsync(CancellationToken cancellationToken);
        public Task StopAsync(CancellationToken cancellationToken);
    }

    三、应用启动流程

    由于ASP.NET Core应用是由GenericWebHostService服务承载的,所以启动应用程序本质上就是启动这个承载服务。承载GenericWebHostService在启动过程中的处理流程基本上体现在如下所示的StartAsync方法中,该方法中刻意省略了一些细枝末节的实现,如输入验证、异常处理、诊断日志事件的发送等。

    internal class GenericWebHostService : IHostedService
    {
        public Task StartAsync(CancellationToken cancellationToken)
        {
            //1. 设置监听地址
            var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>();
            var addresses = serverAddressesFeature?.Addresses;
            if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0)
            {
                var urls = Configuration[WebHostDefaults.ServerUrlsKey];
                if (!string.IsNullOrEmpty(urls))
                {
                    serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
    
                    foreach (var value in urls.Split(new[] { ';' },StringSplitOptions.RemoveEmptyEntries))
                    {
                        addresses.Add(value);
                    }
                }
            }
    
            //2. 构建中间件管道
            var builder = ApplicationBuilderFactory.CreateBuilder(Server.Features);
            Action<IApplicationBuilder> configure = Options.ConfigureApplication;
            foreach (var filter in StartupFilters.Reverse())
            {
                configure = filter.Configure(configure);
            }
            configure(builder);
            var handler = builder.Build();
    
            //3. 创建HostingApplication对象
            var application = new HostingApplication(handler, Logger, DiagnosticListener, HttpContextFactory);
    
            //4. 启动服务器
            return Server.StartAsync(application, cancellationToken);
        }
    }

    我们将实现在GenericWebHostService类型的StartAsync方法中用来启动应用程序的流程划分为如下4个步骤。

    • 设置监听地址:服务器的监听地址是通过IServerAddressesFeature接口表示的特性来承载的,所以需要将配置提供的监听地址列表和相关的PreferHostingUrls选项(表示是否优先使用承载系统提供地址)转移到该特性中。
    • 构建中间件管道:通过调用IWebHostBuilder对象和注册的Startup类型的Configure方法针对中间件的注册会转换成一个Action<IApplicationBuilder>对象,并复制给配置选项GenericWebHostServiceOptions的ConfigureApplication属性。GenericWebHostService承载服务会利用注册的IApplicationBuilderFactory工厂创建出对应的IApplicationBuilder对象,并将该对象作为参数调用这个Action<IApplicationBuilder>对象就能将注册的中间件转移到IApplicationBuilder对象上。但在此之前,注册IStartupFilter对象的Configure方法会优先被调用,IStartupFilter对象针对前置中间件的注册就体现在这里。代表注册中间件管道的RequestDelegate对象最终通过调用IApplicationBuilder对象的Build方法返回。
    • 创建HostingApplication对象:在得到代表中间件管道的RequestDelegate之后,GenericWebHostService对象进一步利用它创建出HostingApplication对象,该对象对于服务器来说就是用来处理由它接收请求的应用程序。
    • 启动服务器:将创建出的HostingApplication对象作为参数调用作为服务器的IServer对象的StartAsync方法后,服务器随之被启动。此后,服务器绑定到指定的地址监听抵达的请求,并为接收的请求创建出对应的HttpContext上下文,后续中间件将在这个上下文中完成各自对请求的处理任务。请求处理结束之后,生成的响应最终通过服务器回复给客户端。

    四、关闭应用

    关闭GenericWebHostService服务之后,只需要按照如下方式关闭服务器即可。除此之外,StopAsync方法还会利用EventSource的形式发送相应的事件,我们在前面针对诊断日志的演示可以体验此功能。

    internal class GenericWebHostService : IHostedService
    {
        public async Task StopAsync(CancellationToken cancellationToken) => Server.StopAsync(cancellationToken);
    }

    请求处理管道[1]: 模拟管道实现
    请求处理管道[2]: HttpContext本质论
    请求处理管道[3]: Pipeline = IServer +  IHttpApplication<TContext
    请求处理管道[4]: 中间件委托链
    请求处理管道[5]: 应用承载[上篇
    请求处理管道[6]: 应用承载[下篇]

  • 相关阅读:
    Python操作Excel
    JMeter生成UUID方式
    JMeter之Beanshell用法
    JMeter后置处理器
    JMeter后置处理器
    Python之正则匹配 re库
    read(),readline() 和 readlines() 比较
    Python的位置参数、默认参数、关键字参数、可变参数之间的区别
    调查管理系统 -(6)自定义Struts2的拦截器&自定义UserAware接口&Action中模型赋值问题&Hibernate懒加载问题
    调查管理系统 -(5)Struts2配置&用户注册/登录/校验
  • 原文地址:https://www.cnblogs.com/artech/p/inside-pipeline-05.html
Copyright © 2020-2023  润新知