首页 > 其他分享 >NetCore 入门 (七) : 承载系统

NetCore 入门 (七) : 承载系统

时间:2022-08-27 15:23:14浏览次数:52  
标签:承载 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
From: https://www.cnblogs.com/renzhsh/p/16630620.html

相关文章

  • NetCore 入门 (八) : 管道
    1.入门ASP.NETCore是一个Web开发平台,而不是一个单纯的开发框架。这是因为它具有一个极具扩展性的请求处理管道,我们可以通过对这个管道的定制来满足各种场景下的HTTP处理......
  • NetCore 入门 (一) : 依赖注入
    1.QuickStart1.1安装NuGet包Microsoft.Extensions.DependencyInjection.Abstractions;//抽象依赖包Microsoft.Extensions.DependencyInjection;//具体实现包:::......
  • redis 入门安装流程
    redis安装流程安装linux的Redis[官网下载即可][https://redis.io/download/]一般会移动到opt目录下mvredis-7.0.4/opt在linux系统下安装redis加压命令tar......
  • Taurus.MVC 微服务框架 入门开发教程:项目部署:6、微服务应用程序Docker部署实现多开。
    系列目录:本系列分为项目集成、项目部署、架构演进三个方向,后续会根据情况调整文章目录。开源地址:https://github.com/cyq1162/Taurus.MVC本系列第一篇:Taurus.MVCV3.......
  • JPA 入门实战(3)--Spring Boot 中使用 JPA
    本文主要介绍在SpringBoot中使用JPA的方法(暂不使用spring-data-jpa),相关的环境及软件信息如下:SpringBoot2.6.10、JPA2.2、eclipselink2.7.10。1、原生使用该......
  • 10个快速入门Query函数使用的Pandas的查询示例
    转载:https://mp.weixin.qq.com/s/TJStQDtUfOOXtb__cpivDgpandas.的query函数为我们提供了一种编写查询过滤条件更简单的方法,特别是在的查询条件很多的时候,在本文中整理了1......
  • Java Servlet 入门: 问题系列:Filter中通过HttpServletRequest.getParts()获取不到上传
    问题:一开始以为Servlet 没有提供对文件读取的相关内容。后来发现,HttpServletRequest中有getParts方法,可以获取上传的文件。再后发,经过反复测试,发现都读不到相关内容。......
  • JavaSE-Day01-Java入门
    Java入门计算机语言发展史机器语言——二进制汇编语言——指令代替二进制高级语言——面向对象、面向过程Java特性和优势简单面向对象可移植——虚拟机高性能......
  • idea入门 及 优化 (链接)
    创建newprojectsrc中创建class文件psvm快捷输入==publicstaticvoidmain(String[]args){    }sout快捷输入==System.out.println();   IDEA最......
  • Golang入门基础1
    Golang入门基本的项目结构go的环境搭建比较简单就直接跳过了工程结构如下每一个go程序都需要依赖一个包现在写一个简单的程序packagemainimport"fmt"funcmain(......