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

Original: [Core the ASP.NET framework. 3 Uncovered] 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. In this part excerpt from the upcoming book "ASP.NET Core 3 framework decrypt", interested friends can "" ASP.NET Core 3 framework Secret "readers, Welcome to" join book readers

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 = log;
        _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

We are " a Mini version of dependency injection framework to create a simple version called Cat dependency injection framework, and" "in the third-party dependency injection framework for adaptation " to create a IServiceProviderFactory for it <TContainerBuilder> achieved, specific types CatServiceProvider, then we show you how to rely on third parties to integrate with the Cat injection framework by registering this CatServiceProvider. If you are using Cat framework, we can mark MapToAttribute characteristics of the type of service on the way to define the service registration information. In the demo program was created in such a way that we use the definition of the three services (Foo, Bar and Baz) and corresponding interface (IFoo, IBar and 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 as illustrated below serve our demo application that is hosted. We injected three services defined above in the constructor, the constructor provides debug assertion to ensure that these three services are successfully injected.

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;
}

In the service of carrying the program shown below, we create a HostBuilder object and registered FakeHostedService services to be borne by calling ConfigureServices method. Our next call UseServiceProviderFactory method of CatServiceProvider completed the registration of, and then calls the Register method CatBuilder completed the bulk of the service registration for the entrance of the assembly. When we call HostBuilder Build method of construction of the target host as Host and start it, FakeHostedService hosted service will automatically be created and started. (Source code here download)

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/lonelyxmas/p/12451799.html