Allbet:源码剖析.Net中Host主机的构建历程

usdt支付接口

www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

前言

本篇文章着重讲一下在.Net中Host主机的构建历程,依旧延续之前文章的思绪,着重解说其源码,若是有不知道有哪些用法的同砚可以点击这里,空话不多说,咱们直接进入正题

Host构建历程

下图是我自己整理的Host构建历程以及内里包罗的知识点我都以链接的形式放上来,人人可以看下图,也许领会下历程(由于知识点过多,以是只能分上下两张图了):

图中标识的相关知识点毗邻如下(ps:与编号对应):

  • 1 环境变量 点击这里
  • 2 下令行参数 点击这里
  • 3 默认设置 点击这里
  • 4 用户隐秘数据 点击这里
  • 5 默认logging 点击这里
  • 6 使用承载启动程序集 点击这里
  • 7 自界说设置 点击这里
  • 8 依赖注入 点击这里
  • 9 应用启动后台义务 点击这里
  • 10 中央件构建 点击这里
  • 11 线程spinwait 点击这里

以上就是笔者在源码阅读阶段,其总结的自我感受主要的知识点在微软文档中的对应位置。

AllbetGmaing客户端下载

欢迎进入AllbetGmaing客户端下载(www.aLLbetgame.us),欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

源码剖析

这部门笔者凭证上图中的四大块划分举行源码剖析,可能篇幅对照长,其主要是对源代码增添了自己明白的注释,以是读者在阅读的历程中,要多注重源码中的注释(ps:展示出的代码不是所有代码,而只是主要代码哦,每个小节总结的点都是根据代码顺序注释)

初始化默认设置ConfigDefaultBuilder

public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[] args)
{
    //设置程序运行路径
    builder.UseContentRoot(Directory.GetCurrentDirectory());
    builder.ConfigureHostConfiguration(config =>
    {
        //添加获取环境变量的前缀
        config.AddEnvironmentVariables(prefix: "DOTNET_");
        //添加下令行参数
        if (args is { Length: > 0 })
        {
            config.AddCommandLine(args);
        }
    });

    builder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        //宿主机环境信息
        IHostEnvironment env = hostingContext.HostingEnvironment;
        //是否在文件改变时重新加载,默认是True
        bool reloadOnChange = GetReloadConfigOnChangeValue(hostingContext);

        //默认添加的设置文件(格外添加以环境变量为名称的文件)
        config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange);
        //若是是开发环境,而且应用程序的应用名称不是空字符串,则加载用户隐秘,默认true(主要是为了差异开发职员的设置差异)
        if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 })
        {
            var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
            if (appAssembly is not null)
            {
                config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange);
            }
        }
        
        //这里再次执行是为了让环境变量和下令行参数的设置优先级提高(后加载的key/value获取时优先级最高)
        //添加其他环境变量
        config.AddEnvironmentVariables();

        //添加下令行参数
        if (args is { Length: > 0 })
        {
            config.AddCommandLine(args);
        }
    })
    .ConfigureLogging((hostingContext, logging) =>
    {
        //判断操作系统
        bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
        if (isWindows)
        {
            //添加过滤规则,捕捉warning日志
            logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
        }
        //获取Logging设置
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
        //添加输出到控制台
        logging.AddConsole();
        //添加将debug日志输出到控制台
        logging.AddDebug();
        //添加写入的事宜源
        logging.AddEventSourceLogger();

        if (isWindows)
        {
            //添加事宜日志
            logging.AddEventLog();
        }
        //添加链路追踪选项
        logging.Configure(options =>
        {
            options.ActivityTrackingOptions =
                ActivityTrackingOptions.SpanId |
                ActivityTrackingOptions.TraceId |
                ActivityTrackingOptions.ParentId;
        });
    })
    .UseDefaultServiceProvider((context, options) =>
    {
        bool isDevelopment = context.HostingEnvironment.IsDevelopment();
        //依赖注入相关校验
        options.ValidateScopes = isDevelopment;
        options.ValidateOnBuild = isDevelopment;
    });
    return builder;
}

源码总结:

  • 设置程序执行路径以及获取环境变量和加载下令行参数。
  • 凭证环境变量加载appsettings.json,加载用户隐秘数据(仅开发环境)。
  • 接着又加载环境变量和下令行参数(这里为什么又加载了一次呢?是由于这它们执行的顺序是纷歧样的,尔后加载的会笼罩前面加载的Key/Value,前面加载主要是确定当前运行的环境变量以及用户自界说的下令行参数,后面是为确保通过key获取value的时刻能够获取到准确的值)。
  • 接下来就主要是设置默认Log,若是是开发环境,依赖注入相关的设置默认开启(验证scope是否被用于singleton,验证是否在挪用时代就确立所有服务至缓存)。

初始化主机启动设置ConfigureWebHostDefaults

public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options)
{
    _builder = builder;
    var configBuilder = new ConfigurationBuilder()
        .AddInMemoryCollection();

    //添加以ASPNETCORE_开头的环境变量(ps:判断当前环境是谁人环境)
    if (!options.SuppressEnvironmentConfiguration)
    {
        configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_");
    }
    //这里主要加载环境变量
    _config = configBuilder.Build();

    _builder.ConfigureHostConfiguration(config =>
    {
        //将上面的设置加载进来
        config.AddConfiguration(_config);

        //通过设置和特征加载分外的Config(或者不加载设置),通过继续IHostingStartup无侵入性加载。
        ExecuteHostingStartups();
    });
    //将上面Startup中Config的设置放到Build阶段加载
    _builder.ConfigureAppConfiguration((context, configurationBuilder) =>
    {
        if (_hostingStartupWebHostBuilder != null)
        {
            var webhostContext = GetWebHostBuilderContext(context);
            _hostingStartupWebHostBuilder.ConfigureAppConfiguration(webhostContext, configurationBuilder);
        }
    });

    //增添注入的服务
    _builder.ConfigureServices((context, services) =>
    {
        var webhostContext = GetWebHostBuilderContext(context);
        var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];

        //注入一些其他服务
        services.AddSingleton(webhostContext.HostingEnvironment);
        services.AddSingleton((AspNetCore.Hosting.IHostingEnvironment)webhostContext.HostingEnvironment);
        services.AddSingleton<IApplicationLifetime, GenericWebHostApplicationLifetime>();

        services.Configure<GenericWebHostServiceOptions>(options =>
        {
            options.WebHostOptions = webHostOptions;
            options.HostingStartupExceptions = _hostingStartupErrors;
        });

        services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore"));
        services.TryAddSingleton<DiagnosticSource>(sp => sp.GetRequiredService<DiagnosticListener>());
        services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore"));

        services.TryAddSingleton<IHttpContextFactory, DefaultHttpContextFactory>();
        services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
        services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>();

        _hostingStartupWebHostBuilder?.ConfigureServices(webhostContext, services);

        //可以通过设置的方式查找程序集加载StartUp,然则默认只会加载最后一个StartUp
        if (!string.IsNullOrEmpty(webHostOptions.StartupAssembly))
        {
            try
            {
                var startupType = StartupLoader.FindStartupType(webHostOptions.StartupAssembly, webhostContext.HostingEnvironment.EnvironmentName);
                UseStartup(startupType, context, services);
            }
            catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
            {
                var capture = ExceptionDispatchInfo.Capture(ex);

                services.Configure<GenericWebHostServiceOptions>(options =>
                {
                    options.ConfigureApplication = app =>
                    {
                        capture.Throw();
                    };
                });
            }
        }
    });
}
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
    //提供.netCore 静态Web资产(ps:说真话这里不知道有什么用)
    builder.ConfigureAppConfiguration((ctx, cb) =>
    {
        if (ctx.HostingEnvironment.IsDevelopment())
        {
            StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
        }
    });
    //使用 Kestrel 设置反向署理
    builder.UseKestrel((builderContext, options) =>
    {
        options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
    })
    .ConfigureServices((hostingContext, services) =>
    {
        //设置启动的Url
        services.PostConfigure<HostFilteringOptions>(options =>
        {
            if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
            {
                var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
            }
        });
        services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(
                    new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
        services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
        //用来获取客户端的IP地址
        if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase))
        {
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
                options.KnownNetworks.Clear();
                options.KnownProxies.Clear();
            });
            services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
        }
        //添加路由设置
        services.AddRouting();
    })
    //默认使用IIS
    .UseIIS()
    //使用历程内的托管模式
    .UseIISIntegration();
}

这部门内容可能会多点,源码总结:

  • 添加Memory缓存,添加ASPNETCORE_开头的环境变量。
  • 凭证用户的设置,来加载分外的StartUp中Config设置,然则它的参数是IWebHostBuilder,这部门可以参考微软文档StartUp的部门。
  • 若是有存在这些设置的话,则统一放到Build阶段加载。
  • 加载web主机需要的注入的服务,以及判断是否需要通历程序集来加载StartUp,而且添加一个程序启动时挪用的服务(这里主要是构建HttpContext执行管道)。
  • 引用Kestrel,继续路由和IIS,而且默认使用历程内托管。
  • 加载用户自界说的其他设置,例如默认的挪用UseStartup方式。

凭证指定设置最先初始化主机Build

public class HostBuilder : IHostBuilder
{
  private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
  private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
  private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
  private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
  private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
  private bool _hostBuilt;
  private IConfiguration _hostConfiguration;
  private IConfiguration _appConfiguration;
  private HostBuilderContext _hostBuilderContext;
  private HostingEnvironment _hostingEnvironment;
  private IServiceProvider _appServices;
  private PhysicalFileProvider _defaultProvider;

  public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();

  public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
  {
      _configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
      return this;
  }

  public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
  {
      _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
      return this;
  }

  public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
  {
      _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
      return this;
  }

  public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
  {
      _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
      return this;
  }

  public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
  {
      _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
      return this;
  }

  public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
  {
      _configureContainerActions.Add(new ConfigureContainerAdapter<TContainerBuilder>(configureDelegate
          ?? throw new ArgumentNullException(nameof(configureDelegate))));
      return this;
  }

  public IHost Build()
  {
      //只能执行一次这个方式
      if (_hostBuilt)
      {
          throw new InvalidOperationException(SR.BuildCalled);
      }
      _hostBuilt = true;

      using var diagnosticListener = new DiagnosticListener("Microsoft.Extensions.Hosting");
      const string hostBuildingEventName = "HostBuilding";
      const string hostBuiltEventName = "HostBuilt";

      if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuildingEventName))
      {
          Write(diagnosticListener, hostBuildingEventName, this);
      }

      //执行Host设置(应用程序执行路径,加载_dotnet环境变量,获取下令行参数,加载预设置)
      BuildHostConfiguration();
      //设置主机环境变量
      CreateHostingEnvironment();
      //构建HostBuilderContext实例
      CreateHostBuilderContext();
      //构建程序设置(加载appsetting.json,环境变量,下令行参数等)
      BuildAppConfiguration();
      //组织容器,注入服务
      CreateServiceProvider();

      var host = _appServices.GetRequiredService<IHost>();
      if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuiltEventName))
      {
          Write(diagnosticListener, hostBuiltEventName, host);
      }

      return host;
  }

  private static void Write<T>(
      DiagnosticSource diagnosticSource,
      string name,
      T value)
  {
      diagnosticSource.Write(name, value);
  }

  private void BuildHostConfiguration()
  {
      IConfigurationBuilder configBuilder = new ConfigurationBuilder()
          .AddInMemoryCollection();

      foreach (Action<IConfigurationBuilder> buildAction in _configureHostConfigActions)
      {
          buildAction(configBuilder);
      }
      //本质是执行ConfigureProvider中的Load方式,加载对应设置
      _hostConfiguration = configBuilder.Build();
  }

  private void CreateHostingEnvironment()
  {
      //设置环境变量
      _hostingEnvironment = new HostingEnvironment()
      {
          ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
          EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
          ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
      };

      if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
      {
          _hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
      }
      //程序运行路径
      _hostingEnvironment.ContentRootFileProvider = _defaultProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
  }

  private void CreateHostBuilderContext()
  {
      _hostBuilderContext = new HostBuilderContext(Properties)
      {
          HostingEnvironment = _hostingEnvironment,
          Configuration = _hostConfiguration
      };
  }

  private void BuildAppConfiguration()
  {
      //对于已经加载过的设置不再重新加载
      IConfigurationBuilder configBuilder = new ConfigurationBuilder()
          .SetBasePath(_hostingEnvironment.ContentRootPath)
          .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);

      //注重这里是AppConfig
      foreach (Action<HostBuilderContext, IConfigurationBuilder> buildAction in _configureAppConfigActions)
      {
          buildAction(_hostBuilderContext, configBuilder);
      }
      _appConfiguration = configBuilder.Build();
      //将新的设置赋值给config
      _hostBuilderContext.Configuration = _appConfiguration;
  }

  private void CreateServiceProvider()
  {
      var services = new ServiceCollection();
      services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
      services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
      services.AddSingleton(_hostBuilderContext);
      services.AddSingleton(_ => _appConfiguration);
      services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
      services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
      services.AddSingleton<IHostLifetime, ConsoleLifetime>();
      services.AddSingleton<IHost>(_ =>
      {
          return new Internal.Host(_appServices,
              _hostingEnvironment,
              _defaultProvider,
              _appServices.GetRequiredService<IHostApplicationLifetime>(),
              _appServices.GetRequiredService<ILogger<Internal.Host>>(),
              _appServices.GetRequiredService<IHostLifetime>(),
              _appServices.GetRequiredService<IOptions<HostOptions>>());
      });
      services.AddOptions().Configure<HostOptions>(options => { options.Initialize(_hostConfiguration); });
      services.AddLogging();
      //主要加载分外注入的服务
      foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
      {
          configureServicesAction(_hostBuilderContext, services);
      }
      //这里返回object,主要是为了保留扩展,让用户自界说的依赖注入框架能够运行。
      object containerBuilder = _serviceProviderFactory.CreateBuilder(services);
      foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
      {
          containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
      }

      _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);

      if (_appServices == null)
      {
          throw new InvalidOperationException(SR.NullIServiceProvider);
      }
      //可能是想先把IConfiguration加载到内存中
      _ = _appServices.GetService<IConfiguration>();
  }
}

在上面的两个小单元可以看出,所有的组织是以委托的方式,最后都加载到HostBuilder内部的委托聚集中,源码总结:

  • 加载环境变量和下令行参数。
  • 构建HostingEnvironment工具。
  • 构建HostBuilderContext工具,内里包罗设置和执行环境。
  • 加载appsettings.json,环境变量和下令行参数等。
  • 注入一些必须的服务,加载日志设置,WebHost内里注入的服务,加载StartUp内里ConfigService内里的服务,以及其他的一些注入的服务,构建容器(最后那里获取IConfiguration预测可能是想先缓存到根容器吧)。

运行主机Run

public async Task StartAsync(CancellationToken cancellationToken = default)
{
    _logger.Starting();

    using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _applicationLifetime.ApplicationStopping);
    CancellationToken combinedCancellationToken = combinedCancellationTokenSource.Token;

    //应用程序启动和关闭事宜
    await _hostLifetime.WaitForStartAsync(combinedCancellationToken).ConfigureAwait(false);

    combinedCancellationToken.ThrowIfCancellationRequested();
    _hostedServices = Services.GetService<IEnumerable<IHostedService>>();
    //主要是执行一些后台义务,可以重写启动和关闭时要做的操作
    foreach (IHostedService hostedService in _hostedServices)
    {
        //立刻执行的义务,例如构建管道就是在这里
        await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
        //执行一些后台义务
        if (hostedService is BackgroundService backgroundService)
        {
            _ = TryExecuteBackgroundServiceAsync(backgroundService);
        }
    }
    //通知应用程序启动乐成
    _applicationLifetime.NotifyStarted();

    //程序启动
    _logger.Started();
}

源码总结:

  • 监听程序的启动关闭事宜。
  • 最先执行Hosted服务或者加载后台执行的义务。
  • 通过TaskCompletionSource来连续监听Token,hold住历程。

总结

通过模板来构建的.Net泛型主机,实在已经可以知足大部门的要求,而且微软保留大量扩展让用户来自界说,固然你也可以构建其他差其余主机类型(如:Web主机或者控制台程序启动项设置),想领会的可以点击这里。

以上就是笔者通过阅读源码来剖析的程序执行流程,由于篇幅问题没有把所有代码都放出来,着实是太多了,以是只放了部门代码,主要是想给阅读源码的同砚在阅读的时刻找到思绪,可能会有一点错误,还请谈论指正。

  • 评论列表:
  •  usdt提币免手续费(www.usdt8.vip)
     发布于 2021-09-16 00:07:29  回复
  • www.9cx.net)实时更“geng”新发布最新〖xin〗最快最有效的新2足球网《wang》址,包括新2手机网址‘zhi’,新2备“bei”用网址,皇冠「guan」最新“xin”网址,新2足球网址《zhi》,新2网「wang」址大《da》全。觉得缺点什么

添加回复:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。