ASP.NET Core应用本质上是一个服务,这个服务启动了一个网络监听器,这个监听器需要长时间的运行。当这个监听器接收到一个HTTP请求之后,监听器会将这个请求传递给管道进行处理。完成处理后就会生成HTTP响应,并通过这个监听器返回给客户端。
这个长时间运行的服务需要寄宿在托管进程中。而提供这个功能的组件,我们称为Hosting。
Hosting
Hosting所实现的功能就是将一个或多个长时间运行的服务寄宿在托管进程中。由Hosting管理的服务我们称为托管服务。我们可以简单理解Hosting为一个服务托管主机。
任何需要在后台长时间运行的程序,我们都可以按照标准把它定义为一个托管服务然后寄宿在Hosting上。
示例:定义一个托管服务
public class demo19
{
public class SystemClock : IHostedService
{
private Timer _timer;
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(state =>
{
Console.WriteLine($"Current Time:{DateTime.Now.ToLongTimeString()}");
},null,0,1000);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Dispose();
return Task.CompletedTask;
}
}
public static void run()
{
var host = new HostBuilder()
.ConfigureServices(collection => collection.AddHostedService<SystemClock>())
.Build();
host.Run();
}
}
Hosting整合了依赖注入、配置选项、日志等。
Hosting由三个核心对象构成:
通过IHostService接口体现的服务,运行在通过IHost接口标识的数组上。而IHostBuilder接口则表示Host对象构建器。
1. IHostService:托管服务。
源码位置:\runtime\src\libraries\Microsoft.Extensions.Hosting.Abstractions\src\IHostedService.cs
源码:
/// <summary>
/// Defines methods for objects that are managed by the host.
/// </summary>
public interface IHostedService
{
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
Task StartAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
Task StopAsync(CancellationToken cancellationToken);
}
当作为数组的Host对象被启动的时候,它会利用依赖注入激活注册的所有托管服务,并通过start方法来启动他们,当应用程序被关闭的时候,作为数组的Host对象也会被关闭。当Host被关闭的时候由它启动的所有的托管服务也都会调用stop而关闭。
托管服务注册的本质:其实就是讲对应的IHostService实现类给注册到依赖注入框架中。
由于托管服务一般都需要长期运行,知道应用程序被关闭。所以针对这个托管服务的注册一般都采用单例。Hosting为托管服务的注册定义了一个为AddHostedService的扩展方法。
AddHostedService:
源码位置:\runtime\src\libraries\Microsoft.Extensions.Hosting.Abstractions\src\ServiceCollectionHostedServiceExtensions.cs
源码:
public static class ServiceCollectionHostedServiceExtensions
{
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
return services;
}
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services, Func<IServiceProvider, THostedService> implementationFactory)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService>(implementationFactory));
return services;
}
}
从源码可以看出AddHostedService最终调用到是服务集合中的TryAddEnumerable扩展方法。这个方法注册的服务是不会重复的,它是尝试注册,如果重复就不会再注册。
2. IHost:主机对象。
源码位置:\runtime\src\libraries\Microsoft.Extensions.Hosting.Abstractions\src\IHost.cs
源码:
/// <summary>
/// A program abstraction.
/// </summary>
public interface IHost : IDisposable
{
/// <summary>
/// The programs configured services.
/// </summary>
IServiceProvider Services { get; }
/// <summary>
/// Start the program.
/// </summary>
/// <param name="cancellationToken">Used to abort program start.</param>
/// <returns>A <see cref="Task"/> that will be completed when the <see cref="IHost"/> starts.</returns>
Task StartAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Attempts to gracefully stop the program.
/// </summary>
/// <param name="cancellationToken">Used to indicate when stop should no longer be graceful.</param>
/// <returns>A <see cref="Task"/> that will be completed when the <see cref="IHost"/> stops.</returns>
Task StopAsync(CancellationToken cancellationToken = default);
}
一般来说一个应用程序在整个生命周期内只会创建一个Host对象。Host对象的启动或停止一般都是跟随着应用程序的启动和关闭的。一般情况下,我们不需要显式的关闭Host,当应用程序关闭的时候,Host自己就会跟随着应用程序一起停止。通过查看IHost源码也可以看出来,IHost是直接派生自IDisposable接口。所以说当它在关闭的时候,应用程序就会调用它的Dispose方法,做一些额外的释放资源的工作。
在IHost中有一个叫做Services的属性,这个属性的类型是IServiceProvider。这个属性就表示依赖注入的容器。这个对象提供了Host中所有需要服务实例,其中也包含了以这个托管服务接口体现的托管服务。
3. IHostBuilder:主机构建器。
在最上面的Hosting使用示例中可以看出,我们使用了HostBuilder构建出Host之后并没有调用start方法而是使用的run方法。而在IHostService中并未提供run的服务支持。run方法涉及使用了Host的应用生命周期管理,而要了解run方法我们首先需要了解IHostApplicationLifetime主机应用生命周期。
IHostApplicationLifetime:
源码位置:\runtime\src\libraries\Microsoft.Extensions.Hosting.Abstractions\src\IHostApplicationLifetime.cs
源码:
/// <summary>
/// Allows consumers to be notified of application lifetime events. This interface is not intended to be user-replaceable.
/// </summary>
public interface IHostApplicationLifetime
{
/// <summary>
/// Triggered when the application host has fully started.
/// </summary>
CancellationToken ApplicationStarted { get; }
/// <summary>
/// Triggered when the application host is sta