asp.net core 3.x 通用主机是如何承载asp.net core的-上

1、前言

上一篇《asp.net core 3.x 通用主机原理及使用》扯了下3.x中的通用主机,恰好有哥们写了篇《.NET Core 3.1和WorkerServices构建Windows服务》能够当作通用主机的案例来看。本篇主要聊下asp.net core 3.x中是如何使用通用主机来承载asp.net core自己的。html

注:我是.net framework 4.x跳到.net core 3.x的,基本看源码总结的,可能某些地方理解不到位,因此此文只做为参考别全信哈。web

阅读前提(参考老A的博客):数据库

  • 了解配置系统和选项模式
  • 了解通用主机,能够看看上一篇文章
  • 若是能大概理解Builder模式和上下文模式就更好了

目录:json

  • 通用主机回顾
  • 通用主机是如何承载asp.net core的
  • 核心类及使用要点
  • 总结

2、回顾通用主机

2.一、IHost表示通用主机

微软为咱们提供了一个默认实现(Microsoft.Extensions.Hosting.Internal.Host),它内部主要包含:ioc容器(服务提供器)、生命周期事件处理器、日志记录器、和IHostedService集合以及启动和中止方法。api

2.1.一、ioc服务提供器:

用来存储各类服务对象,这个根容器是全部IHostedService共享的,各个IHostedService也能够建立主机的“范围IOC容器”。ioc容器包含微软为咱们塞进去的,和咱们本身塞进去的各类服务组件。在主机配置阶段微软会塞入:缓存

  • HostingEnvironment 主机环境,
    • EnvironmentName主机环境(是开发模式?仍是生产模式?)
    • ApplicationName应用名称
    • ContentRootPath主机的内容根路径
    • ContentRootFileProvider内容提供器
  • HostBuilderContext 主机建立过程当中的上下文对象,在配置主机的多个步骤中传递数据。主要包含:HostingEnvironment和AppConfiguration应用配置对象
  • AppConfiguration 应用配置对象,里面也包含主机配置对象
  • IHostLifetime主机生命周期事件处理器
  • IHostApplicationLifetime 应用生命周期事件处理器
  • Internal.Host 做为默认主机

因此未来咱们定义的类随时能够注入这些对象,这些对象是在HostBuilder中赋值的,请往下看app

2.1.二、生命周期事件处理器

未来主机启动/中止时会触发回调相应的几个方法,好比:主机启动了 应用启动了  应用中止了,主机中止了。咱们能够自定义一个事件处理器加入到ioc容器中来实现生命周期事件的订阅。框架

2.1.三、IHostedService集合

一个IHostedService的实现类就是一个应用。asp.net core自己就是一个应用。asp.net

2.1.四、启动流程

触发相应的生命周期事件、遍历启动全部IHostedServiceide

2.二、HostBuilder表示主机构造器

它负责主机的配置和生成。它定义了几个委托集合,并提供相应的方法容许咱们的代码向集合中加入本身的委托,这些委托主要是用来:

  • 向ioc容器注册服务;
  • 设置主机和应用配置对象的数据源;
  • 配置容器自己(好比替换成别的依赖注入框架);

未来在调用Build生成最终的主机时会:

  • 建立主机配置生成器ConfigurationBuilder,而后回调咱们的代码提供的委托对配置对象的数据源进行设置,最终经过配置对象生成器.Build生成配置对象
  • 建立HostingEnvironment(含义上面有说),默认用配置对象进行赋值,而后回调咱们的代码提供的委托进一步设置主机环境对象
  • 初始化BuilderContext(含义上面有说)
  • 初始化应用配置生成器,套路给主机配置同样
  • 初始化IOC容器,
    • 先把上面说的对象丢入容器中,
    • 注册默认主机, Internal.Host 
    • 注册选项模式须要的几个服务,
    • 注册日志系统须要的服务
    • 而后回调咱们的委托,咱们的委托能够注入各类主机须要的服务
    • 配置ioc容器自己,咱们对微软提供的ioc进行配置,或干脆替换为第三方依赖注入框架
  • 嘴周从容器中解析获得主机

2.三、Host.CreateDefaultBuilder进一步简化主机的配置和建立

  • 建立HostBuilder
  • 设置内容根为当前应用程序根目录
  • 将以"DOTNET_"的环境变量和命令行参数(若是有)做为“主机配置”的数据源
  • 以如下数据做为“应用配置”的数据源
    • json文件appsettings.json和appsettings.{env.EnvironmentName}.json
    • 若是是开发模式,则添加一个特殊的json文件做为数据源,这个文件存储如:帐号 密码 数据库链接字符串等比较机密的信息(文件查找路径有点复杂,后期补充)
    • 添加环境变量做为配置源(没细看,也许是整个系统的环境变量)
    • 尝试添加命令行参数配置源
  • 添加日志记录系统须要的服务以及相关的配置
  • 使用默认的ServiceProvider,当是开发模式时:在建立对象阶段开启依赖注入范围验证

以上为通用主机的大体内容,先有个印象,未来须要扩展时在研究源码

3、通用主机是如何承载asp.net core的

asp.net core 3.x开始直接使用通用主机,主要思路是对通用主机作跟web相关配置(添加跟web相关的配置源和注册服务)关键是会将GenericWebHostService注册到ioc容器中。对于通用主机来讲所谓的应用就是一个实现了IHostedService的类。GenericWebHostService就是这样一个类,它就基本表明了asp.net core。对于通用主机来讲asp.net core不过是一个应用而已。未来通用主机启动时天然是启动GenericWebHostService

注:GenericWebHostService会在启动时建立ApplicationBuilder,而后对其进行配置(中间件管道),而后生成一个Application,最后拿到配置好的IServer(如KestrlServer)而后传入Application并启动它,Server开始监听http请求,后续请求抵达时由Application对象负责将请求传入中间件管道*(大体这么个流程,还没详细去看GenericWebHostService源码)

先来看个图:

 核心任务是:

  1. 建立GenericWebHostBuilder,它是对通用主机构建器HostBuilder的一个包装,提供跟web相关的配置源的设置和服务的注册。构造函数中会作些初始化,默认配置
  2. 经过静态方法Host.ConfigureWebDefaults对GenericWebHostBuilder作进一步的默认配置
  3. 调用用户代码(咱们本身写的)对GenericWebHostBuilder作进一步配置

因此文章后面部分咱们将这个用来承载asp.net core的通用主机称为“Web主机”,把通用主机配置器HostBuilder的包装类GenericWebHostBuilder称为“Web主机配置器”

4、GenericWebHostBuilder

它包装了HostBuilder,因此能够把它理解为一个特殊的HostBuilder,特殊在它提供web相关的配置api,本质上仍是向通用主机配置对象HostBuilder添加各类服务和配置源

4.一、GetSetting、UseSetting

GenericWebHostBuilder有个IConfiguration类型的属性,能够把它理解为跟web相关的配置对象,它会做为通用主机的配置源,因为通用主机的配置源又是应用配置的数据源,所以最后:应用配置对象 = 通用主机配置对象 + (web主机配置对象)GenericWebHostBuilder._config  + 应用配置对象;
_config在构造函数中被初始化,惟一数据源是以"ASPNETCORE_"为前缀的环境变量
另外配置对象还在ExecuteHostingStartups被使用到,后面会详细讲
GetSetting、UseSetting这俩方法分别从_config读取和写入配置,因此咱们能够在配置主机时更方便的向配置中加入一些值,也能够替换一些值来影响一些组件的配置

因此咱们能够在配置web主机时经过UseSetting来快速设置/替换某些配置值,也能够在任意能得到GenericWebHostBuilder的地方调用GetSetting方便的获取配置值

4.二、IWebHostEnvironment、WebHostOptions、WebHostBuilderContext

在web主机配置过程当中的多个步骤中常常会使用到这几个对象,咱们对web主机进行配置时,提供的委托的参数也常常会携带者节参数,所以看看这几个东西是啥。

IWebHostEnvironment
web主机环境相关,它还继承IHostEnvironment,因此它包含如下内容:

  • EnvironmentName 主机运行环境,是开发?生产?
  • ApplicationName 应用名
  • ContentRootPath 内容根,默认是应用程序所在目录
  • ContentRootFileProvider 内容根对应的文件提供器
  • WebRootPath  web根,默认wwwroot,
  • WebRootFileProvider web根对应的文件提供器

WebHostOptions
web主机选项对象,虽然是这个名字,可是并无应用选项模式,且它是internal修饰的,所以咱们写代码一般不会访问到它,可是它在GenericWebHostBuilder有些做用的
它内部包含根web相关的配置:

  • ApplicationName:应用程序名
  • PreventHostingStartup、HostingStartupAssemblies、HostingStartupExcludeAssemblies:插件/模块相关属性,参考《asp.net core 3.x 模块化开发之HostingStartup
  • Environment:应用程序当前运行,是开发模式?是生产环境?
  • StartupAssembly:启动类Startup所在程序集
  • WebRoot:web静态资源目录,一般对应那个wwwroot目录
  • ContentRootPath:内容根路径,一般对应应用程序所在目录,

WebHostBuilderContext
= IWebHostEnvironment + IConfiguration

private WebHostBuilderContext GetWebHostBuilderContext(HostBuilderContext context)
GenericWebHostBuilder经过这个方法来建立WebHostBuilderContext,web主机配置器内部多个方法都会调用此方法。此方法执行过程大体步骤以下:

  • 使用主机配置器上下文的配置对象来建立一个WebHostOptions,因此WebHostOptions上面那些属性都是经过配置来赋值的,因为HostBuilderContext的配置对象如今=主机配置对象 + 应用配置对象 + web主机配置对象,可想而知,咱们能够经过多种途径来配置WebHostOptions的属性
  • 建立WebHostBuilderContext
    • Configuration = context.Configuration,
    • HostingEnvironment = new HostingEnvironment()
  • 经过HostingEnvironmentExtensions.Initialize对WebHostBuilderContext.HostingEnvironment进行初始化,其实就是将WebHostOptions中相应的属性赋值上去
  • 最后HostBuilderContext和WebHostOptions被存储到HostBuilderContext.Properties缓存起来

因此咱们平时能够经过多种途径来配置内容根、web根、应用名、运行环境(开发?生成?)
也能够在配置web主机时的委托中来经过WebHostBuilderContext来访问到这些属性和对应的文件提供器
因为IHostingEnvironment会以单例注册到容器,所以咱们未来能够直接注入HostingEnvironment或者web主机环境对象

4.三、public IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure)

调用此方法传入一个委托,这个委托主要用来配置中间件管道,未来通用主机在启动时会启动表明asp.net core的GenericWebHostService,这时咱们这个委托就会被调用。因此配置管道的代码是在HostBuilder.Build().Run()这个Run阶段执行,并非在Build这步

这个方法跟UseStartup(下面会说)是冲突的,意思只能用其中一个,

要了解这个方法的原理,得先说说GenericWebHostServiceOptions,它是一个选项对象,看定义:

 1 namespace Microsoft.AspNetCore.Hosting{
 3     internal class GenericWebHostServiceOptions{
 5        public Action<IApplicationBuilder> ConfigureApplication { get; set; }
 6        public WebHostOptions WebHostOptions { get; set; }
 8        public AggregateException HostingStartupExceptions { get; set; }
10     }
11 }

此对象应用了选项模式,(第5行)ConfigureApplication其实就表明了那个用来配置中间件管道的委托

 1 public IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure){
 2             _builder.ConfigureServices((context, services) => {
 3                 services.Configure<GenericWebHostServiceOptions>(options => {
 4                     var webhostBuilderContext = GetWebHostBuilderContext(context);
 5                     options.ConfigureApplication = app => configure(webhostBuilderContext, app);
 6                 });
 7             });
 8 
 9             return this;
10         }

因此不管是Startup中的Configre方法,仍是这里传入的委托,最终都会以一个委托的形式赋值到GenericWebHostServiceOptions.ConfigureApplication属性上,而这个委托未来在主机启动阶段被调用,最终实现容许用户配置中间件管道的目的

为何要提供两种配置中间件管道的方式呢?由于直接在Program.main里配置更简单,可是封装性很差,经过单独的Startup类更清晰。

因此咱们配置中间件管道时除了能够在Startup.Configre中配置,也能够直接在Program.main里配置主机时经过GenericWebHostBuilder.Configure进行配置

 

未完待续....

相关文章
相关标签/搜索