ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NetCore 入门 (七) : 承载系统

2022-08-27 15:33:40  阅读:139  来源: 互联网

标签:承载 return 入门 NetCore private static ._ IHostBuilder public


1. 介绍

承载系统(Hosting,也就是泛型主机),提供了一种通用的功能:承载一个或多个需要长时间运行(Long-Running)的服务。

承载系统是基于依赖注入开发的,并自动集成了以下特性:

  • Configuration
  • Options
  • Logging
  • FileProvider

1.1 NuGet包

Microsoft.Extensions.Hosting.Abstractions; // 抽象依赖包
Microsoft.Extensions.Hosting; //默认实现包

1.2 入门示例

我们来演示一个简单的承载服务,来定时采集当前进程的性能指标。

定义承载服务

承载服务通过IHostedService接口定义,接口的StartAsync方法和StopAsync方法用来启动和关闭服务。

// 性能指标
public class Performances
{
   public int Processor { get => _random.Next(1, 8); }

   public long Memory { get => _random.Next(10, 100); }

   public long Network { get => _random.Next(10, 100); }

   public override string ToString()
   {
       return $"CPU: {Processor * 10}%; Memory: {Memory}M; Network: {Network} Kb/s";
   }

   private readonly Random _random = new Random();
}
public class MetricsCollector : IHostedService // 定义承载服务
{

    private readonly ILogger<MetricsCollector> _logger;
    private IDisposable _timer;
    private Performances performances;

    public MetricsCollector(ILogger<MetricsCollector> logger) // 通过依赖注入,获取ILogger
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken) // 启动服务
    {
        Console.WriteLine("[MetricsCollector] Starting ... ...");
        performances = new Performances();
        _timer = new Timer( // 通过定时器Timer每隔5秒分发一次性能信息
            state => Delivery((Performances)state),
            performances,
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(5)
           );
        return Task.CompletedTask;
    }

    private void Delivery(Performances performances)  // 分发性能指标
    {
        Console.WriteLine($"[{DateTimeOffset.Now}] {performances}");
        _logger.LogInformation(performances.ToString());
    }

    public Task StopAsync(CancellationToken cancellationToken) // 关闭服务
    {
        Console.WriteLine("[MetricsCollector] Stopped");
        _timer?.Dispose();
        return Task.CompletedTask;
    }
}

启动承载系统

承载系统是承载服务运行的宿主,通过IHost接口表示。该对象采用Builder模式,由对应的IHostBuilder对象来创建。

static void Main(string[] args)
{
    IHostBuilder builder = new HostBuilder()
      .ConfigureServices(services =>
      {
          services.AddHostedService<MetricsCollector>(); // 添加 IHostedService 服务
      })
      .ConfigureLogging(builder => builder.AddConsole()); // 配置日志

    IHost host = builder.Build();

    host.Run();
}

输出结果

第一部分:系统启动

[MetricsCollector] Starting ... ...
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: E:\ItBooks\NetCoreTutorial\ASP.Net Core\HostingTutorial\bin\Debug\netcoreapp3.1\

第二部分:服务运行

[2020/7/2 10:56:17 +08:00] CPU: 70%; Memory: 50M; Network: 70 Kb/s
info: HostingTutorial.MetricsCollector[0]
      CPU: 30%; Memory: 81M; Network: 70 Kb/s
[2020/7/2 10:56:22 +08:00] CPU: 50%; Memory: 53M; Network: 89 Kb/s
info: HostingTutorial.MetricsCollector[0]
      CPU: 60%; Memory: 45M; Network: 60 Kb/s
[2020/7/2 10:56:27 +08:00] CPU: 60%; Memory: 68M; Network: 61 Kb/s
info: HostingTutorial.MetricsCollector[0]
      CPU: 10%; Memory: 89M; Network: 15 Kb/s
[2020/7/2 10:56:32 +08:00] CPU: 20%; Memory: 33M; Network: 15 Kb/s
info: HostingTutorial.MetricsCollector[0]
      CPU: 60%; Memory: 61M; Network: 77 Kb/s

第三部分:收到Ctrl+C信号后退出

info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
[MetricsCollector] Stopped

2. 承载模型

承载模型的设计采用了典型的Builder模式,如下图所示。

承载模型

承载模型主要由3个核心对象组成:通过IHostBuilder构建IHost对象。代表宿主的IHost对象承载多个IHostService服务。

2.1 IHostService

IHostService接口代表需要长时间运行的服务。

public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

作为宿主的IHost对象被启动时,它会利用依赖注入框架激活每一个注册的IHostedService服务,并通过调用StartAsync方法来激活它们。当应用程序关闭时,作为宿主的IHost对象会被关闭,由它承载的每个IHostedService服务对象的StopAsync方法也随之调用。

2.2 IHost

宿主对象通过IHost接口表示。一般来说,一个应用程序在整个生命周期内只会创建一个IHost对象。启动和关闭应用程序本质上就是启动和关闭宿主对象IHost

public interface IHost : IDisposable
{
    IServiceProvider Services { get; }
    Task StartAsync(CancellationToken cancellationToken = default);
    Task StopAsync(CancellationToken cancellationToken = default);
}
  • Services:依赖注入容器,该对象提供了承载服务过程中所需的所有服务实例。
  • StartAsyncStopAsync:启动和关闭宿主对象。

2.3 IHostBuilder

通过IHostBuilder建立IHost对象。

public interface IHostBuilder
{
    // 建立IHost对象
    IHost Build();

    ...
}

2.4 生命周期

这一节介绍几个与生命周期相关的接口。

2.4.1 IHostApplictionLifetime

IHostApplictionLifetime接口用来控制应用程序的生命周期。

public interface IHostApplicationLifetime
{
    CancellationToken ApplicationStarted { get; }
    CancellationToken ApplicationStopping { get; }
    CancellationToken ApplicationStopped { get; }

    void StopApplication();
}
  • ApplicationStarted:通知 consumers(服务的消费者) 应用程序已启动。
  • ApplicationStopping:通知 consumers 应用程序正在关闭。
  • ApplicationStopped:通知 consumers 应用程序已关闭。
  • StopApplication:发送 “关闭应用程序” 的指令。

2.4.2 ApplicationLifetime

IHostApplictionLifetime接口的默认实现。

public class ApplicationLifetime : IHostApplicationLifetime
{
    private readonly CancellationTokenSource _startedSource = new CancellationTokenSource();
    private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource();
    private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource();

    public CancellationToken ApplicationStarted { get => _startedSource.Token; }
    public CancellationToken ApplicationStopping { get => _stoppingSource.Token; }
    public CancellationToken ApplicationStopped { get => _stoppedSource.Token; }

    private void ExecuteHandlers(CancellationTokenSource cancel)
    {
        if (cancel.IsCancellationRequested)
        {
            return;
        }
        cancel.Cancel(false);
    }

    public void StopApplication()
    {
        CancellationTokenSource stoppingSource = this._stoppingSource;
        lock (stoppingSource)
        {
            this.ExecuteHandlers(this._stoppingSource);
        }
    }

    ...
}

在此基础上扩展了2个方法,用来变更应用程序的状态。

public class ApplicationLifetime : IHostApplicationLifetime
{
    public void NotifyStarted() => ExecuteHandlers(this._startedSource);
    public void NotifyStopped() => ExecuteHandlers(this._stoppedSource);
}
示例演示

下面通过一个简单的示例来演示应用程序的生命周期。

// 定义承载服务
public class LifetimeService : IHostedService
{
    private readonly IHostApplicationLifetime lifetime;

    public LifetimeService(IHostApplicationLifetime applicationLifetime)
    {
        lifetime = applicationLifetime;

        lifetime.ApplicationStarted.Register(() => // 添加事件回调
        {
            Console.WriteLine($"[{DateTimeOffset.Now}] Application Started"); 
        });

        lifetime.ApplicationStopping.Register(() =>
        {
            Console.WriteLine($"[{DateTimeOffset.Now}] Application Stopping");
        });

        lifetime.ApplicationStopped.Register(() =>
        {
            Console.WriteLine($"[{DateTimeOffset.Now}] Application Stopped");
        });
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Task.Run(() =>
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
            lifetime.StopApplication(); // 5秒 后关闭应用程序
        });

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}
static void Main(string[] args)
{
    new HostBuilder()
    .ConfigureServices(services => services.AddHostedService<LifetimeService>())
    .Build()
    .Run();
}

输出结果

[2020/7/7 12:31:13 +08:00] Application Started
[2020/7/7 12:31:18 +08:00] Application Stopping
[2020/7/7 12:31:18 +08:00] Application Stopped

2.4.3 IHostLifetime

与生命周期相关的另一个接口,它的核心功能就是:

  • 监听 应用程序关闭退出信号Ctrl+C or SIGTERM),并调用IHostApplicationLifetime.StopApplication()方法关闭应用程序。
public interface IHostLifetime
{
    Task WaitForStartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}
  • WaitForStartAsync: 在Host对象的StartAsync方法中被调用。
  • StopAsync: 在Host对象的StopAsync方法中被调用。

2.4.4 ConsoleLifetime

IHostLifetime接口的默认实现。

public class ConsoleLifetime : IHostLifetime, IDisposable
{
    private IHostApplicationLifetime ApplicationLifetime { get; }

    public Task WaitForStartAsync(CancellationToken cancellationToken)
    {
        AppDomain.CurrentDomain.ProcessExit += this.OnProcessExit; // 监听应用程序的退出信号
        Console.CancelKeyPress += this.OnCancelKeyPress; // 监听退出(ctrl + c)按键
        return Task.CompletedTask;
    }

    private void OnProcessExit(object sender, EventArgs e)
    {
        this.ApplicationLifetime.StopApplication();
        System.Environment.ExitCode = 0;
    }

    private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
        e.Cancel = true;
        this.ApplicationLifetime.StopApplication();
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

2.5 Host

IHost接口的实现。

internal class Host : IHost, IDisposable, IAsyncDisposable
{
    public IServiceProvider Services { get; }

    private readonly IHostLifetime _hostLifetime;
    private readonly ApplicationLifetime _applicationLifetime;
    private IEnumerable<IHostedService> _hostedServices;

    public Host(IServiceProvider services, 
                IHostApplicationLifetime applicationLifetime, 
                ILogger<Host> logger, 
                IHostLifetime hostLifetime, 
                IOptions<HostOptions> options)
    {
        ...
    }
}

2.5.1 StartAsync

public async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    using (CancellationTokenSource combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
        cancellationToken, 
        this._applicationLifetime.ApplicationStopping)
    )
    {
        CancellationToken combinedCancellationToken = combinedCancellationTokenSource.Token;

        // 1. 调用IHostLifetime的WaitForStartAsync
        await this._hostLifetime.WaitForStartAsync(combinedCancellationToken);
        combinedCancellationToken.ThrowIfCancellationRequested();

        // 2. 启动所有的承载服务
        this._hostedServices = this.Services.GetService<IEnumerable<IHostedService>>();
        foreach (IHostedService hostedService in this._hostedServices)
        {
            await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
        }

        // 3. 通知:应用程序已启动
        ApplicationLifetime applicationLifetime = this._applicationLifetime;
        if (applicationLifetime != null)
        {
            applicationLifetime.NotifyStarted();
        }
    }
}

2.5.2 StopAsync

public async Task StopAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    CancellationToken token = cancellationToken.Token;

    // 1. 通过ApplicationLifetime关闭应用程序。如果正在关闭中,什么都不做
    ApplicationLifetime applicationLifetime = this._applicationLifetime;
    if (applicationLifetime != null)
    {
        applicationLifetime.StopApplication();
    }

    // 2. 停止所有的承载服务
    IList<Exception> exceptions = new List<Exception>();
    if (this._hostedServices != null)
    {
        foreach (IHostedService hostedService in this._hostedServices.Reverse<IHostedService>())
        {
            token.ThrowIfCancellationRequested();
            try
            {
                await hostedService.StopAsync(token).ConfigureAwait(false);
            }
            catch (Exception item)
            {
                exceptions.Add(item);
            }
        }
    }

    // 3. 调用IHostLifetime的StopAsync
    token.ThrowIfCancellationRequested();
    await this._hostLifetime.StopAsync(token);

    // 4. 通知:应用程序已关闭
    ApplicationLifetime applicationLifetime2 = this._applicationLifetime;
    if (applicationLifetime2 != null)
    {
        applicationLifetime2.NotifyStopped();
    }
}

2.5.3 扩展方法

public static class HostingAbstractionsHostExtensions
{
    public static void Start(this IHost host)
    {
        host.StartAsync(default(CancellationToken)).GetAwaiter().GetResult();
    }

    // timeout超时后关闭host
    public static Task StopAsync(this IHost host, TimeSpan timeout)
    {
        return host.StopAsync(new CancellationTokenSource(timeout).Token);
    }

    public static void Run(this IHost host)
    {
        host.RunAsync(default(CancellationToken)).GetAwaiter().GetResult();
    }

    public static void WaitForShutdown(this IHost host)
    {
        host.WaitForShutdownAsync(default(CancellationToken)).GetAwaiter().GetResult();
    }
}

2.5.4 RunAsync

从入门示例中我们可以看到:宿主Host的启动并不是直接调用StartAsync,而是Run方法。原因在于StartAsyncStopAsync仅仅负责Host的启动和关闭,并没有把Host的整个生命周期串联起来,对于StopApplication()发出的信号也没有做处理。Run方法就是解决这个问题的。

public static class HostingAbstractionsHostExtensions
{
    public static async Task RunAsync(this IHost host, CancellationToken token = default(CancellationToken))
    {
        await host.StartAsync(token);

        await host.WaitForShutdownAsync(token);
    }
}

2.5.5 WaitForShutdownAsync

public static class HostingAbstractionsHostExtensions
{
    public static async Task WaitForShutdownAsync(this IHost host, CancellationToken token = default(CancellationToken))
    {
        IHostApplicationLifetime service = host.Services.GetService<IHostApplicationLifetime>();
        token.Register(delegate(object state)
        {
            ((IHostApplicationLifetime)state).StopApplication();
        }, service);

        TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>(
            TaskCreationOptions.RunContinuationsAsynchronously);

        CancellationToken cancellationToken = service.ApplicationStopping;

        cancellationToken.Register(delegate(object obj)
        {
            ((TaskCompletionSource<object>)obj).TrySetResult(null);
        }, taskCompletionSource);

        // 阻塞主线程,直到IHostApplictionLifetime.StopApplication()方法被调用,此语句才返回
        await taskCompletionSource.Task;

        // 关闭host
        cancellationToken = default(CancellationToken);
        await host.StopAsync(cancellationToken);
    }
}

WaitForShutdownAsync方法是控制生命周期的核心。Host启动后,主线程(启动host的线程)将一直阻塞在await taskCompletionSource.Task;语句,此时的主线程将拒绝执行其他的任何任务,保证Host处于运行状态。

方法StopApplication()被调用后,IHostApplicationLifetime发出关闭应用程序的信号ApplicationStopping,此时语句await taskCompletionSource.Task;将返回,WaitForShutdownAsync方法继续往下执行,即关闭Host

3. 宿主配置

3.1 宿主环境

3.1.1 IHostEnvironment

IHostEnvironment表示承载服务的部署环境。

public interface IHostEnvironment
{
    string EnvironmentName { get; set; }
    string ApplicationName { get; set; }
    string ContentRootPath { get; set; }
    IFileProvider ContentRootFileProvider { get; set; }
}
  • EnvironmentName:环境名称。开发预发产品是3种典型的承载环境,分别用DevelopmentStagingProduction来命名。
  • ApplicationName:应用程序的名称。
  • ContentRootPath:存放内容文件(Content File) 的根目录的绝对路径。内容文件包含应用程序启动程序集、依赖程序包、静态文件(javascript、css、图片)等。
  • ContentRootFileProvider: 指向ContentRootPathIFileProvider对象,用来读取文件内容。

3.1.2 HostingEnvironment

IHostEnvironment接口的默认实现。HostingEnvironment对象在IHostBuilder.Builder()方法中构建。

public class HostingEnvironment : IHostEnvironment
{
	public string EnvironmentName { get; set; }
	public string ApplicationName { get; set; }
	public string ContentRootPath { get; set; }
	public IFileProvider ContentRootFileProvider { get; set; }
}

3.1.3 扩展

public static class Environments
{
	public static readonly string Development = "Development";
	public static readonly string Staging = "Staging";
	public static readonly string Production = "Production";
}
public static class HostingEnvironmentExtensions
{
    public static bool IsEnvironment(this IHostingEnvironment hostingEnvironment, string environmentName)
    {
        return string.Equals(hostingEnvironment.EnvironmentName, environmentName, StringComparison.OrdinalIgnoreCase);
    }

    public static bool IsDevelopment(this IHostingEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(Environments.Development);
    }

    public static bool IsStaging(this IHostingEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(Environments.Staging);
    }

    public static bool IsProduction(this IHostingEnvironment hostingEnvironment)
    {
        return hostingEnvironment.IsEnvironment(Environments.Production);
    }
}

3.2 整合第三方框架

承载系统为第三方依赖注入框架的整合,提供了便利的接口。

::: tip 参考

3.2.1 IServiceFactoryAdapter

IServiceProviderFactory接口的适配器。从功能上讲,和IServiceProviderFactory一致,都具有CreateBuilderCreateServiceProvider两个方法。但设置IServiceFactoryAdapter的目的在于:在承载系统和IServiceProviderFactory接口之间架设一个适配器,提供更多配置的可能性。

internal interface IServiceFactoryAdapter
{
	object CreateBuilder(IServiceCollection services);

	IServiceProvider CreateServiceProvider(object containerBuilder);
}

ServiceFactoryAdapter

内部使用的适配器。允许根据HostBuilderContext选择IServiceProviderFactory

internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
{
    private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
    private readonly Func<HostBuilderContext> _contextResolver;
    private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;


    public ServiceFactoryAdapter(IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
    {
        this._serviceProviderFactory = serviceProviderFactory;
    }

    public ServiceFactoryAdapter(Func<HostBuilderContext> contextResolver, 
        Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
    {
        this._contextResolver = contextResolver;
        this._factoryResolver = factoryResolver;
    }

    public object CreateBuilder(IServiceCollection services)
    {
        if (this._serviceProviderFactory == null)
        {
            this._serviceProviderFactory = this._factoryResolver(this._contextResolver());
        }
        return this._serviceProviderFactory.CreateBuilder(services);
    }

    public IServiceProvider CreateServiceProvider(object containerBuilder)
    {
        return this._serviceProviderFactory.CreateServiceProvider((TContainerBuilder)((object)containerBuilder));
    }
}

3.2.2 IConfigureContainerAdapter

对依赖注入容器TContainerBuilder提供配置。

internal interface IConfigureContainerAdapter
{
	void ConfigureContainer(HostBuilderContext hostContext, object containerBuilder);
}

ConfigureContainerAdapter

内部使用的适配器。

internal class ConfigureContainerAdapter<TContainerBuilder> : IConfigureContainerAdapter
{
    private Action<HostBuilderContext, TContainerBuilder> _action;

	public ConfigureContainerAdapter(Action<HostBuilderContext, TContainerBuilder> action)
	{
		this._action = action;
	}

	public void ConfigureContainer(HostBuilderContext hostContext, object containerBuilder)
	{
		this._action(hostContext, (TContainerBuilder)((object)containerBuilder));
	}
}

3.3 IHostBuilder

通过IHostBuilder对宿主服务进行前期配置。

public interface IHostBuilder
{
    // 建立IHost对象
    IHost Build();

    // 配置Configuration
    IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate);
    IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate);

    // 添加依赖注入服务
    IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate);

    // 整合第三方依赖注入框架
    IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate);
    IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory);
    IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
            Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory
        );
}

3.3.1 HostBuilderContext

public class HostBuilderContext
{
    public HostBuilderContext(IDictionary<object, object> properties) => Properties = properties;

    public IHostEnvironment HostingEnvironment { get; set; }
    public IConfiguration Configuration { get; set; }
    public IDictionary<object, object> Properties { get; }
}
  • HostingEnvironment:当前的承载环境。
  • Configuration:当前的配置。
  • Properties:用作数据共享的字典。

3.3.2 HostBuilder

IHostBuilder接口的默认实现。

public class HostBuilder : IHostBuilder
{
    private IConfiguration _hostConfiguration;      // 针对宿主的配置
    private IConfiguration _appConfiguration;       // 针对应用程序的配置
    private HostBuilderContext _hostBuilderContext; // builder上下文
    private HostingEnvironment _hostingEnvironment; // 宿主环境
    private IServiceProvider _appServices;          // IServiceProvider对象

    private List<Action<IConfigurationBuilder>> _configureHostConfigActions;
    private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions;
    private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions;
    private List<IConfigureContainerAdapter> _configureContainerActions;

    private IServiceFactoryAdapter _serviceProviderFactory = // 默认初始值
        new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());

    ...
}

3.4 Configuration的配置

public class HostBuilder : IHostBuilder
{
    // 针对宿主的配置
    public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
    {
        List<Action<IConfigurationBuilder>> configureHostConfigActions = this._configureHostConfigActions;
        configureHostConfigActions.Add(configureDelegate);
        return this;
    }

    // 针对应用程序的配置
    public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        List<Action<HostBuilderContext, IConfigurationBuilder>> configureAppConfigActions = this._configureAppConfigActions;
        configureAppConfigActions.Add(configureDelegate);
        return this;
    }
}

::: tip IConfiguration

HostBuilder对象中,存在2个IConfiguration对象:

  • _hostConfiguration:针对宿主的配置,主要用于创建HostingEnvironment
  • _appConfiguration:针对应用程序的配置。我们通过依赖注入服务获得的是就是这个对象。

最终_hostConfiguration要合并到_appConfiguration对象中。

:::

3.5 依赖注入的配置

3.5.1 添加服务注册

  • ConfigureServices
public class HostBuilder : IHostBuilder
{
    // 添加服务注册
    public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
    {
        List<Action<HostBuilderContext, IServiceCollection>> configureServicesActions = this._configureServicesActions;
        configureServicesActions.Add(configureDelegate);
        return this;
    }
}
public static class HostingHostBuilderExtensions
{
    public static IHostBuilder ConfigureServices(this IHostBuilder hostBuilder, Action<IServiceCollection> configureDelegate)
    {
        return hostBuilder.ConfigureServices(delegate(HostBuilderContext context, IServiceCollection collection)
        {
            configureDelegate(collection);
        });
    }
}

3.5.2 添加承载服务

  • AddHostedService
public static class ServiceCollectionHostedServiceExtensions
{
	public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services) 
        where THostedService : class, IHostedService
	{
		services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
		return services;
	}

	public static IServiceCollection AddHostedService<THostedService>(
        this IServiceCollection services, 
        Func<IServiceProvider, THostedService> implementationFactory
        ) where THostedService : class, IHostedService
	{
		services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService>(implementationFactory));
		return services;
	}
}

3.5.3 第三方框架配置

  • ConfigureContainer
  • UseServiceProviderFactory
  • UseDefaultServiceProvider
public class HostBuilder : IHostBuilder
{
    public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
    {
        List<IConfigureContainerAdapter> configureContainerActions = this._configureContainerActions;
        configureContainerActions.Add(new ConfigureContainerAdapter<TContainerBuilder>(configureDelegate));
        return this;
    }

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
    {
        this._serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory);
        return this;
    }

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
        Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory
        )
    {
        Func<HostBuilderContext> contextResolver = () => this._hostBuilderContext;
        this._serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(contextResolver, factory);
        return this;
    }
}
public static class HostingHostBuilderExtensions
{
    public static IHostBuilder UseDefaultServiceProvider(this IHostBuilder hostBuilder, 
        Action<ServiceProviderOptions> configure)
    {
        return hostBuilder.UseDefaultServiceProvider(delegate(HostBuilderContext context, ServiceProviderOptions options)
        {
            configure(options);
        });
    }

    public static IHostBuilder UseDefaultServiceProvider(this IHostBuilder hostBuilder, 
        Action<HostBuilderContext,ServiceProviderOptions> configure)
    {
        return hostBuilder.UseServiceProviderFactory<IServiceCollection>(delegate(HostBuilderContext context)
        {
            ServiceProviderOptions serviceProviderOptions = new ServiceProviderOptions();
            configure(context, serviceProviderOptions);
            return new DefaultServiceProviderFactory(serviceProviderOptions);
        });
    }
}

3.6 Logging的配置

  • ConfigureLogging
public static class HostingHostBuilderExtensions
{
    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, 
        Action<HostBuilderContext, ILoggingBuilder> configureLogging)
    {
        return hostBuilder.ConfigureServices(delegate(HostBuilderContext context, IServiceCollection collection)
        {
            collection.AddLogging(delegate(ILoggingBuilder builder)
            {
                configureLogging(context, builder);
            });
        });
    }

    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<ILoggingBuilder> configureLogging)
    {
        return hostBuilder.ConfigureServices(delegate(HostBuilderContext context, IServiceCollection collection)
        {
            collection.AddLogging(delegate(ILoggingBuilder builder)
            {
                configureLogging(builder);
            });
        });
    }
}

3.7 宿主环境的配置

  • UseEnvironment:指定环境名称
  • UseContentRoot:指定内容文件根目录
public static class HostingHostBuilderExtensions
{
    public static IHostBuilder UseEnvironment(this IHostBuilder hostBuilder, string environment)
    {
        return hostBuilder.ConfigureHostConfiguration(delegate(IConfigurationBuilder configBuilder)
        {
            KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[1];
            
            array[0] = new KeyValuePair<string, string>(HostDefaults.EnvironmentKey, environment);

            configBuilder.AddInMemoryCollection(array);
        });
    }

    public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string contentRoot)
    {
        return hostBuilder.ConfigureHostConfiguration(delegate(IConfigurationBuilder configBuilder)
        {
            KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[1];
            
            array[0] = new KeyValuePair<string, string>(HostDefaults.ContentRootKey, contentRoot);

            configBuilder.AddInMemoryCollection(array);
        });
    }

}

HostDefaults

public static class HostDefaults
{
	public static readonly string ApplicationKey = "applicationName";

	public static readonly string EnvironmentKey = "environment";

	public static readonly string ContentRootKey = "contentRoot";
}

3.8 CreateDefaultBuilder

承载系统提供了一个默认的配置方法,该方法配置了一些常用的功能,包括日志、配置等等。

public static class Host
{
    public static IHostBuilder CreateDefaultBuilder() => CreateDefaultBuilder(null);

    public static IHostBuilder CreateDefaultBuilder(string[] args)
    {
        HostBuilder hostBuilder = new HostBuilder();
        hostBuilder.UseContentRoot(Directory.GetCurrentDirectory());  // 1 程序集的起始路径为根目录

        hostBuilder.ConfigureHostConfiguration(delegate(IConfigurationBuilder config)
        {
            config.AddEnvironmentVariables("DOTNET_"); // 2 添加前缀DOTNET_
            if (args != null)
            {
                config.AddCommandLine(args); // 3 添加命令行
            }
        });

        hostBuilder.ConfigureAppConfiguration(delegate(HostBuilderContext hostingContext, IConfigurationBuilder config)
        {
            IHostEnvironment hostingEnvironment = hostingContext.HostingEnvironment;

            // 4 添加配置文件且开启监控
            config
                .AddJsonFile("appsettings.json", true, true)
                .AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true);

            if (hostingEnvironment.IsDevelopment() && !string.IsNullOrEmpty(hostingEnvironment.ApplicationName))
            {
                Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
                if (assembly != null)
                {
                    config.AddUserSecrets(assembly, true); // 5 开发环境 开启 UserSecrets
                }
            }

            config.AddEnvironmentVariables();
            if (args != null)
            {
                config.AddCommandLine(args);
            }
        });
        
        hostBuilder.ConfigureLogging(delegate(HostBuilderContext hostingContext, ILoggingBuilder logging)
        {
            bool flag = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
            if (flag)
            {
                logging.AddFilter((LogLevel level) => level >= LogLevel.Warning);
            }
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
            logging.AddEventSourceLogger();
            if (flag)
            {
                logging.AddEventLog();
            }
        });
        
        hostBuilder.UseDefaultServiceProvider(delegate(HostBuilderContext context, ServiceProviderOptions options)
        {
            // 6 开发环境开启服务验证
            bool flag = context.HostingEnvironment.IsDevelopment();
            options.ValidateScopes = flag;
            options.ValidateOnBuild = flag;
        });

        return hostBuilder;
    }
}

4. 构建宿主

4.1 Build方法

public class HostBuilder : IHostBuilder
{
    public IHost Build()
    {
        this.BuildHostConfiguration();
        this.CreateHostingEnvironment();
        this.CreateHostBuilderContext();
        this.BuildAppConfiguration();
        this.CreateServiceProvider();
        return this._appServices.GetRequiredService<IHost>();
    }

    private IConfiguration _hostConfiguration;      // 针对宿主的配置
    private IConfiguration _appConfiguration;       // 针对应用程序的配置
    private HostBuilderContext _hostBuilderContext; // builder上下文
    private HostingEnvironment _hostingEnvironment; // 宿主环境
    private IServiceProvider _appServices;          // IServiceProvider对象

    private List<Action<IConfigurationBuilder>> _configureHostConfigActions;
    private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions;
    private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions;
    private List<IConfigureContainerAdapter> _configureContainerActions;
    private IServiceFactoryAdapter _serviceProviderFactory;
}

宿主的构建经历了以下6个步骤:

  1. 建立宿主Configuration
  2. 创建宿主环境HostingEnvironment
  3. 创建Builder上下文
  4. 建立应用Configuration
  5. 创建依赖注入服务
  6. 通过依赖注入服务获取宿主对象

4.2 建立宿主Configuration

private void BuildHostConfiguration()
{
	IConfigurationBuilder configurationBuilder = new ConfigurationBuilder().AddInMemoryCollection();
	foreach (Action<IConfigurationBuilder> action in this._configureHostConfigActions)
	{
		action(configurationBuilder);
	}
	this._hostConfiguration = configurationBuilder.Build();
}

4.3 创建宿主环境

private void CreateHostingEnvironment()
{
	this._hostingEnvironment = new HostingEnvironment
	{
		ApplicationName = this._hostConfiguration[HostDefaults.ApplicationKey],

		EnvironmentName = (this._hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production),

		ContentRootPath = this.ResolveContentRootPath(
            this._hostConfiguration[HostDefaults.ContentRootKey], 
            AppContext.BaseDirectory)
	};
	if (string.IsNullOrEmpty(this._hostingEnvironment.ApplicationName))
	{
		HostingEnvironment hostingEnvironment = this._hostingEnvironment;
		Assembly entryAssembly = Assembly.GetEntryAssembly();
		hostingEnvironment.ApplicationName = ((entryAssembly != null) ? entryAssembly.GetName().Name : null);
	}
	this._hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(this._hostingEnvironment.ContentRootPath);
}

private string ResolveContentRootPath(string contentRootPath, string basePath)
{
	if (string.IsNullOrEmpty(contentRootPath))
	{
		return basePath;
	}
	if (Path.IsPathRooted(contentRootPath))
	{
		return contentRootPath;
	}
	return Path.Combine(Path.GetFullPath(basePath), contentRootPath);
}
  • ApplicationName:从_hostConfiguration中读取key=applicationName的应用程序的名称。默认为程序集的名称。

  • EnvironmentName:从_hostConfiguration中读取key=environment的环境名称。默认为Production

  • ContentRootPath:从_hostConfiguration中读取key=contentRoot的内容文件根目录。默认为程序集的根目录。

  • ContentRootFileProvider:以ContentRootPath为根目录的FileProvider

  • HostDefaults

ContentRootPath 的设置规则:

  1. 如果在配置中没有指定key=contentRoot,采用默认的程序集的根目录
  2. 如果在配置中指定了key=contentRoot且是绝对路径,则采用指定的路径。
  3. 如果在配置中指定了key=contentRoot且是相对路径,则采用key=contentRoot的绝对路径(相对于程序集根目录的路径)。

4.4 创建Builder上下文

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

::: warning
此时的Configuration对象为宿主Configuration。
:::

4.5 建立应用Configuration

private void BuildAppConfiguration()
{
	IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
        .SetBasePath(this._hostingEnvironment.ContentRootPath) // 指定配置文件的起始目录
        .AddConfiguration(this._hostConfiguration, true); // 合并宿主Configuration

	foreach (Action<HostBuilderContext, IConfigurationBuilder> action in this._configureAppConfigActions)
	{
		action(this._hostBuilderContext, configurationBuilder);
	}

	this._appConfiguration = configurationBuilder.Build();

	this._hostBuilderContext.Configuration = this._appConfiguration;
}

::: tip

  1. ConfigureAppConfiguration()方法中,HostBuilderContext对象的Configuration_hostConfiguration
  2. _hostConfiguration合并到_appConfiguration对象。
  3. 更新HostBuilderContext对象的Configuration_appConfiguration

参考 ConfigureAppConfiguration()
:::

4.6 创建依赖注入服务

private void CreateServiceProvider()
{
	ServiceCollection serviceCollection = new ServiceCollection();

    // 1. 添加内部服务
	serviceCollection.AddSingleton(this._hostingEnvironment);
	serviceCollection.AddSingleton(this._hostBuilderContext);
	serviceCollection.AddSingleton((IServiceProvider _) => this._appConfiguration);
	serviceCollection.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
	serviceCollection.AddSingleton<IHostLifetime, ConsoleLifetime>();
	serviceCollection.AddSingleton<IHost, Host>();

	serviceCollection.AddOptions(); // 启用Options模式
	serviceCollection.AddLogging(); // 启用日志

    // 2. 配置外部的注册服务
	foreach (Action<HostBuilderContext, IServiceCollection> action in this._configureServicesActions)
	{
		action(this._hostBuilderContext, serviceCollection);
	}

    // 3. 整合第三方依赖注入框架
	object containerBuilder = this._serviceProviderFactory.CreateBuilder(serviceCollection);
	foreach (IConfigureContainerAdapter configureContainerAdapter in this._configureContainerActions)
	{
		configureContainerAdapter.ConfigureContainer(this._hostBuilderContext, containerBuilder);
	}

    // 4. 创建IServiceProvider对象
	this._appServices = this._serviceProviderFactory.CreateServiceProvider(containerBuilder);
}

标签:承载,return,入门,NetCore,private,static,._,IHostBuilder,public
来源: https://www.cnblogs.com/renzhsh/p/16630620.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有