[ASP.NET Core 3 frame Disclosure] bearer service system [5]: bearer service start process [Part I]

We in the " overall design [Part I] " and " the overall design [next] " through the introduction of IHostedService, IHost and IHostBuider three interfaces allow readers to carry on the service model friends have a general understanding. Next, we turn to concrete from the abstract to see carrying system for realization of the model is how the landing. To understand the model of the default bearer to achieve only need to understand the interface and the default IHost IHostBuilder implementation type on it. As can be seen from the UML shown below, the default implementation of the interface of these two types are Host and HostBuilder, Benpian will highlight both types.

10-9

First, the service host

Host is the default type of IHost implementation of the interface, it is just the definition of a type of internal NuGet package "Microsoft.Extensions.Hosting" in, because we will be the last involving public static type of the same name in this section, in the prone of confusion, we will call it "instance type Host" to show the difference. Before the formal presentation realization of Host type, we have first to understand two related types, one of which is HostOptions related configuration options bearer. The following code fragment, HostOptions only contains a unique attribute ShutdownTimeout means closed Host object timeout, its default value is 5 seconds.

public class HostOptions
{
    public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
}

We in the " overall design [Part I] " has met a bearer-related application lifecycle IHostApplicationLifetime interfaces, Host also relates to another type associated with the life cycle of IHostLifetime interface. When we call the Host object StartAsync method it will start, which will first call WaitForStartAsync method IHostLifetime services. When StopAsync Host object methods in the implementation process, if it successfully closed all hosted services, registration services StopAsync IHostLifetime method is called.

public interface IHostLifetime
{
    Task WaitForStartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

In the " bearer service [next] long running time of" a log demo program, the program will start after three output on the console log level Information, the contents of which the first log was "Application started. Press Ctrl + C to shut down. ", the two that are output environment information of the current carrying path of the root directory and store content files. Before the application is closed, it will appear on the console a message that says "Application is shutting down ..." log. The four above-log scale output on the console effects reflected in the next figure.

10-10

Four log shown above are output as the target ConsoleLifetime, ConsoleLifetime achieve IHostLifetime type interface. In addition to output status information associated with the current carrying applications other than in the form of logs, capture for the Cancel key (Ctrl + C), and then closes the current application function is also implemented in ConsoleLifetime type. ConsoleLifetime configuration options defined in ConsoleLifetimeOptions type employed, the type of unique attribute member is used to determine whether or not the four SuppressStatusMessages log to be output.

public class ConsoleLifetime : IHostLifetime, IDisposable
{
    public ConsoleLifetime(IOptions<ConsoleLifetimeOptions> options, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime);
    public ConsoleLifetime(IOptions<ConsoleLifetimeOptions> options, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);

    public Task StopAsync(CancellationToken cancellationToken);
    public Task WaitForStartAsync(CancellationToken cancellationToken);
    public void Dispose();
}

public class ConsoleLifetimeOptions
{
    public bool SuppressStatusMessages { get; set; }
}

The following code fragment shows the simplified Host type definition. Host type constructor dependency injection a series of services, including as a dependency injection container IServiceProvider object for logging ILogger <Host> objects and to provide options IOptions <HostOptions> objects, and two with the life cycle of related IHostApplicationLifetime objects and IHostLifetime objects. It is worth mentioning that the type required IHostApplicationLifetime object here is ApplicationLifetime, because it needs to call its NotifyStarted and NotifyStopped method to notify subscribers after application startup and shutdown, but these methods are not defined in the interface IHostApplicationLifetime in.

internal class Host : IHost
{
    private readonly ILogger<Host> _logger;
    private readonly IHostLifetime _hostLifetime;
    private readonly ApplicationLifetime _applicationLifetime;
    private readonly HostOptions _options;
    private IEnumerable<IHostedService> _hostedServices;

    public IServiceProvider Services { get; }

    public Host(IServiceProvider services, IHostApplicationLifetime applicationLifetime, ILogger<Host> logger, IHostLifetime hostLifetime, IOptions<HostOptions> options)
    {
        Services = services;
        _applicationLifetime = (ApplicationLifetime)applicationLifetime;
        _logger = logger;
        _hostLifetime = hostLifetime;
        _options = options.Value);
    }

    public async Task StartAsync(CancellationToken cancellationToken = default)
    {
        await _hostLifetime.WaitForStartAsync(cancellationToken);
        cancellationToken.ThrowIfCancellationRequested();
        _hostedServices = Services.GetService<IEnumerable<IHostedService>>();
        foreach (var hostedService in _hostedServices)
        {
            await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);
        }
        _applicationLifetime?.NotifyStarted();
    }

    public async Task StopAsync(CancellationToken cancellationToken = default)
    {
        using (var cts = new CancellationTokenSource(_options.ShutdownTimeout))
        using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken))
        {
            var token = linkedCts.Token;
            _applicationLifetime?.StopApplication();
            foreach (var hostedService in _hostedServices.Reverse())
            {
                await hostedService.StopAsync(token).ConfigureAwait(false);
            }

            token.ThrowIfCancellationRequested();
            await _hostLifetime.StopAsync(token);
            _applicationLifetime?.NotifyStopped();
        }
    }

    public void Dispose() => (Services as IDisposable)?.Dispose();
}

In StartAsync implemented in, Host Objects pioneered a method called WaitForStartAsync IHostLifetime object. If the type of service registration for ConsoleLifetime, it will log output three previously mentioned. Meanwhile, ConsoleLifetime console object also registered key event, and its object is to ensure that when the user presses the cancel key combination (Ctrl + C) applications to be normally closed.

Host objects using a dependency of the extracted object IServiceProvider container object that represents all IHostedService bearer service, and start them by StartAsync method. When all bearer services start properly, NotifyStarted method ApplicationLifetime object is invoked, this time subscribers will receive notification application started. One thing important to note that: All IHostedService object that represents bearer service is "one by one (not concurrent)" is activated, but only after waiting for all bearer services were all started, our application started to be successful. Throughout the startup process, if used as a parameter CancellationToken cancellation request is received, to start the operation aborts.

When StopAsync Host object method is called, it will call ApplicationLifetime object StopApplication method of external notification application is about to be closed, after which it will call StopAsync method for each IHostedService object. NotifyStopped method when all bearer services were successfully closed, Host objects will have to call IHostLifetime object StopAsync and ApplicationLifetime objects. In the Host closing process, if the timeout is exceeded by HostOptions configuration option, or with a cancellation request is received CancellationToken parameters, the process will be aborted.

Second, the setting for the configuration of the system

As a service IHost target host is always out of the building through the corresponding IHostBuilder objects above the corresponding type Host realized IHostBuilder type HostBuilder , next we're going to explore how HostBuilder Host objects are objects built out. In addition to Build method of constructing IHost object, IHostBuilder interface also defines a set of methods so that we can make the appropriate pre-set target IHost final offer, these settings will be cached finally applied to the Build method.

Let's start with HostBuilder settings for configuration of the system. The following code fragment, ConfigureHostConfiguration method for host configuration and oriented ConfigureAppConfiguration delegate object-oriented applications are configured to provide a method of temporarily stored in the corresponding set of objects, the corresponding fields are configureHostConfigActions and configureAppConfigActions.

public class HostBuilder : IHostBuilder
{
    private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
    private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();

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

    public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
    {
        _configureHostConfigActions.Add(configureDelegate);
        return this;
    }

    public IHostBuilder ConfigureAppConfiguration(
        Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        _configureAppConfigActions.Add(configureDelegate);
        return this;
    }
    …
}

Many methods of IHostBuilder interfaces are associated with dependency injection. Set for dependency injection framework is mainly reflected in two aspects: First, using the method of adding ConfigureServices service registration; Second, the use of two UseServiceProviderFactory <TContainerBuilder> method registered IServiceProviderFactory <TContainerBuilder> plant, and the use of ConfigureContainer <TContainerBuilder> the party ContainerBuilder factory creates further settings.

Third, the registration dependent services

As for the setup and configuration of the system, ConfigureServices process to register dependent services Action <HostBuilderContext, IServiceCollection> delegate object is temporarily stored in the same set of fields corresponding to configureServicesActions represented, they will eventually be used in the Build process.

public class HostBuilder : IHostBuilder
{
    private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();

    public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
    {
        _configureServicesActions.Add(configureDelegate);
        return this;
    }
    …
}

In addition to the direct method calls ConfigureServices IHostBuilder service interfaces are registered, we can also call the following methods to complete the registration of these extensions for some special services. Two ConfigureLogging extension method overloads help us registered for logging framework related services, two UseConsoleLifetime extension method is overloaded to add ConsoleLifetime for service registration, two RunConsoleAsync base extension method overloads in the registration ConsoleLifetime services on further Construction and start IHost object as a host.

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

    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<ILoggingBuilder> configureLogging)
    => hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(builder)));

    public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder)
    =>  hostBuilder.ConfigureServices((context, collection) => collection.AddSingleton<IHostLifetime, ConsoleLifetime>());

    public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder, Action<ConsoleLifetimeOptions> configureOptions)
    =>  hostBuilder.ConfigureServices((context, collection) =>
        {
            collection.AddSingleton<IHostLifetime, ConsoleLifetime>();
            collection.Configure(configureOptions);
        });

    public static Task RunConsoleAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
    =>  hostBuilder.UseConsoleLifetime().Build().RunAsync(cancellationToken);

    public static Task RunConsoleAsync(this IHostBuilder hostBuilder, Action<ConsoleLifetimeOptions> configureOptions, CancellationToken cancellationToken = default)
    =>  hostBuilder.UseConsoleLifetime(configureOptions).Build().RunAsync(cancellationToken);
}

Fourth, registration IServiceProviderFactory <TContainerBuilder>

IServiceProvider objects a dependency of the container is always registered IServiceProviderFactory <TContainerBuilder> factory creates. Since UseServiceProviderFactory <TContainerBuilder> registration method IServiceProviderFactory <TContainerBuilder> is a generic object, so HostBuilder converts it to an interface type as follows IServiceFactoryAdapter this adaptation. As shown in the code segment below, it is only to convert ContainerBuilder Object type only. ServiceFactoryAdapter <TContainerBuilder> IServiceFactoryAdapter type is the default interface to achieve.

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)
    => _serviceProviderFactory = serviceProviderFactory;

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

    public object CreateBuilder(IServiceCollection services)
        => _serviceProviderFactory ?? _factoryResolver(_contextResolver()).CreateBuilder(services);

    public IServiceProvider CreateServiceProvider(object containerBuilder)

        => _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder);
}

Define two UseServiceProviderFactory <TContainerBuilder> overloaded shown below, Func IServiceProviderFactory a first method of providing overload <TContainerBuilder> objects and provides a second method overloading <HostBuilderContext, IServiceProviderFactory <TContainerBuilder >> is converted into a ServiceFactoryAdapter <TContainerBuilder> object and stored temporarily by _serviceProviderFactory field. If UseServiceProviderFactory <TContainerBuilder> method is not called, _serviceProviderFactory return field will be created in accordance with the object DefaultServiceProviderFactory ServiceFactoryAdapter <IServiceCollection> object code snippet shown below also reflect this.

public class HostBuilder : IHostBuilder
{
    private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
    private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());

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

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

TContainerBuilder object registration IServiceProviderFactory <TContainerBuilder> factories may be provided by further ConfigureContainer <TContainerBuilder> method, be done by setting the specific Action provided <HostBuilderContext, TContainerBuilder> object. This generic delegate object also needs to do a similar adaptation in order to be stored temporarily, it will eventually be converted into the following IConfigureContainerAdapter interface types, this adaptation is the essence of an object converted TContainerBuilder Object type. ConfigureContainerAdapter <TContainerBuilder> type shown below is the default implementation of this interface.

internal interface IServiceFactoryAdapter
{
    object CreateBuilder(IServiceCollection services);
    IServiceProvider CreateServiceProvider(object containerBuilder);
}

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

internal class ConfigureContainerAdapter<TContainerBuilder> : IConfigureContainerAdapter
{
    private Action<HostBuilderContext, TContainerBuilder> _action;
    public ConfigureContainerAdapter(Action<HostBuilderContext, TContainerBuilder> action)
        => _action = action;
    public void ConfigureContainer(HostBuilderContext hostContext, object containerBuilder)
        => _action(hostContext, (TContainerBuilder)containerBuilder);
}

Is defined as follows ConfigureContainer <TContainerBuilder> methods, we find that the method will provide the Action <HostBuilderContext, TContainerBuilder> selected objects into ConfigureContainerAdapter <TContainerBuilder> objects, and added to the collection represented by configureContainerActions field.

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

Fifth, integration with third-party reliance injection framework

我们在《一个Mini版的依赖注入框架》中创建了一个名为Cat的简易版依赖注入框架,并在《与第三方依赖注入框架的适配》中为它创建了一个IServiceProviderFactory<TContainerBuilder>实现,具体类型为CatServiceProvider,接下来我们演示一下如何通过注册这个CatServiceProvider实现与Cat这个第三方依赖注入框架的整合。如果使用Cat框架,我们可以在服务类型上标注MapToAttribute特性的方式来定义服务注册信息。在创建的演示程序中,我们采用这样的方式定义了三个服务(Foo、Bar和Baz)和对应的接口(IFoo、IBar和IBaz)。

public interface IFoo { }
public interface IBar { }
public interface IBaz { }

[MapTo(typeof(IFoo), Lifetime.Root)]
public class Foo :  IFoo { }

[MapTo(typeof(IBar), Lifetime.Root)]
public class Bar :  IBar { }

[MapTo(typeof(IBaz), Lifetime.Root)]
public class Baz :  IBaz { }

如下所示的FakeHostedService表示我们演示的应用程序承载的服务。我们在构造函数中注入了上面定义的三个服务,构造函数提供的调试断言确保这三个服务被成功注入。

public sealed class FakeHostedService: IHostedService
{
    public FakeHostedService(IFoo foo, IBar bar, IBaz baz)
    {
        Debug.Assert(foo != null);
        Debug.Assert(bar != null);
        Debug.Assert(baz != null);
    }
    public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

在如下所示的服务承载程序中,我们创建了一个HostBuilder对象,并通过调用ConfigureServices方法注册了需要承载的FakeHostedService服务。我们接下来调用UseServiceProviderFactory方法完成了对CatServiceProvider的注册,并在随后调用了CatBuilder的Register方法完成了针对入口程序集的批量服务注册。当我们调用HostBuilder的Build方法构建出作为宿主的Host对象并启动它之后,承载的FakeHostedService服务将自动被创建并启动。(源代码从这里下载)

class Program
{
    static void Main()
    {
        new HostBuilder()
            .ConfigureServices(svcs => svcs.AddHostedService<FakeHostedService>())
            .UseServiceProviderFactory(new CatServiceProviderFactory())
            .ConfigureContainer<CatBuilder>(builder=>builder.Register(Assembly.GetEntryAssembly()))
            .Build()
            .Run();
    }
}

Service bearer system [1]: a long-running service bearer [Part]
service bearer system [2]: bearer services running time [next]
service bearer system [3]: General Design [Part]
service bearer system [4]: general design [Part II]
service bearer system [5]: bearer service start process [Part]
service bearer system [6]: bearer service start process [Part II]

Guess you like

Origin www.cnblogs.com/artech/p/inside-asp-net-core-09-05.html