NetCore 入门 (七) : 承载系统
作者:互联网
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
:依赖注入容器,该对象提供了承载服务过程中所需的所有服务实例。StartAsync
和StopAsync
:启动和关闭宿主对象。
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
orSIGTERM
),并调用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
方法。原因在于StartAsync
和StopAsync
仅仅负责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种典型的承载环境,分别用Development
、Staging
和Production
来命名。ApplicationName
:应用程序的名称。ContentRootPath
:存放内容文件(Content File) 的根目录的绝对路径。内容文件包含应用程序启动程序集、依赖程序包、静态文件(javascript、css、图片)等。ContentRootFileProvider
: 指向ContentRootPath
的IFileProvider
对象,用来读取文件内容。
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
一致,都具有CreateBuilder
和CreateServiceProvider
两个方法。但设置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个步骤:
- 建立宿主Configuration
- 创建宿主环境HostingEnvironment
- 创建Builder上下文
- 建立应用Configuration
- 创建依赖注入服务
- 通过依赖注入服务获取宿主对象
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
。
ContentRootPath 的设置规则:
- 如果在配置中没有指定
key=contentRoot
,采用默认的程序集的根目录。 - 如果在配置中指定了
key=contentRoot
且是绝对路径,则采用指定的路径。 - 如果在配置中指定了
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
- 在
ConfigureAppConfiguration()
方法中,HostBuilderContext
对象的Configuration
为_hostConfiguration
。 - 把
_hostConfiguration
合并到_appConfiguration
对象。 - 更新
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