Explore ASP.Net Core 3.0 series five: introducing IHostLifetime and start interacting clarify Generic Host

Introduction: In this article, I'll show you how to re-build 3.0, as well as some of the benefits resulting ASP.NET Core over generic host. But also it shows 3.0 introduces a new abstract class IHostLifetime, and describes its role in management applications (especially worker services) life cycle. In the second half of the article, I will detail interaction and its role during application startup and shutdown between classes. But also describes in detail the things we usually do not need treatment, even if do not care, but it is for us to understand the principle is also useful!

 

 

A series exploring ASP.NET Core 3.0: new project file, Program.cs and generic host

Explore ASP.Net Core 3.0 Series II: talk ASP.Net Core 3.0 of Startup.cs

Explore ASP.Net Core 3.0 series three: ASP.Net Core 3.0 The Service provider validation

Explore ASP.Net Core 3.0 Series 4: running asynchronous tasks when you start the application in ASP.NET Core 3.0

Explore ASP.Net Core 3.0 Series Six: ASP.NET Core 3.0 new features start structured log information

I. Background: The ASP.NET Core platform to re-universal host

One of the main features of (1) ASP.NET Core 3.0 is based on .NET Generic Host whole have been rewritten. .NET Generic Host ASP.NET Core 2.1 is introduced, is "non-Web" version of WebHost existing ASP.NET Core uses. Generic Host allows you to reuse a lot of DI Microsoft.Extensions in non-Web scenarios, configuration and logging abstraction.

(2) While this is definitely an admirable goal, but there are some problems in the implementation. Generic Host essentially copied many of abstraction required for ASP.NET Core, create a direct equivalent, but using a different namespace. IHostingEnvironment is a good example of the problem - since version 1.0, it already exists in ASP.NET Core in Microsoft.AspNetCore.Hosting in. However, in version 2.1, adding a new IHostingEnvironment in Microsoft.Extensions.Hosting namespace. Even if the interface is the same, but both led to common libraries try to use abstract problem.

(3) using 3.0, ASP.NET Core team to make significant changes to directly address this issue. They do not have to re-write two separate Hosts, but can be re-written in ASP.NET Core stack, so that it is located above the .NET generic host. This means that it can actually reuse the same abstract to solve the above problems. We want to build other non-HTTP stack (eg ASP.NET Core 3.0 introduced gRPC function) is part of the motivation for the move also contributed over generic host.

(4) However, ASP.NET Core 3 carried over generic host "rebuilding" or "re-platform" really means what? Basically, this means that Kestrel Web server (HTTP requests and calls to middleware pipelines) now run as IHostedService. When your application starts, Kestrel now just another service running in the background.

 

Note: It is worth emphasizing is that the existing WebHost and WebHostBuilder you use in ASP.NET Core 2.x applications implemented in 3.0 will not disappear. They are no longer recommended method, but has not been removed, not even marked as obsolete. I hope they will in the next major release is marked as obsolete and therefore worth considering switching.

Simply introduces the background. We have a common host, Kestrel as IHostedService run. However, another function ASP.NET Core 3.0 is introduced IHostLifetime interface that allows the use of other managed model.

 

Two, Worker services and new interfaces IHostLifetime

ASP.NET Core 3.0 introduces the concept of "worker services" and the associated new application template. Worker services designed to provide a long-running application can for you, you can install them as a Windows service or system services. The service has two main functions:

  • They back-office applications to achieve by implementing IHostedService.
  • They IHostLifetime by a class that implements an interface to manage the life cycle of the application.

Below us to create a Worker services, long Sha Yangzi to see:

 

 

 Mouse over BackgroundService F12, you will find, that was it:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Extensions.Hosting
{
    //
    // 摘要:
    //     /// Base class for implementing a long running Microsoft.Extensions.Hosting.IHostedService.
    //     ///
    public abstract class BackgroundService : IHostedService, IDisposable
    {
        private Task _executingTask;

        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

        //
        // 摘要:
        //     /// This method is called when the Microsoft.Extensions.Hosting.IHostedService
        //     starts. The implementation should return a task that represents /// the lifetime
        //     of the long running operation(s) being performed. ///
        //
        // 参数:
        //   stoppingToken:
        //     Triggered when Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)
        //     is called.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that represents the long running operations.
        protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

        //
        // 摘要:
        //     /// Triggered when the application host is ready to start the service. ///
        //
        // 参数:
        //   cancellationToken:
        //     Indicates that the start process has been aborted.
        public virtual Task StartAsync(CancellationToken cancellationToken)
        {
            _executingTask = ExecuteAsync(_stoppingCts.Token);
            if (_executingTask.IsCompleted)
            {
                return _executingTask;
            }
            return Task.CompletedTask;
        }

        //
        // 摘要:
        //     /// Triggered when the application host is performing a graceful shutdown. ///
        //
        // 参数:
        //   cancellationToken:
        //     Indicates that the shutdown process should no longer be graceful.
        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            if (_executingTask != null)
            {
                try
                {
                    _stoppingCts.Cancel();
                }
                finally
                {
                    await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
                }
            }
        }

        public virtual void Dispose()
        {
            _stoppingCts.Cancel();
        }
    }
}

 

Very clear, BackgroundService inherited IHostedService. IHostedService has been around for a long time, and allows you to run background services. The second point is very interesting. IHostLifetime interface is a .NET Core 3.0's new features, it has two methods:

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

In a later section, we will introduce IHostLifetime in detail, but are summarized as follows:

  • GM will call the host startup WaitForStartAsync, which can be used to monitor the shutdown event to start or delay the application until a certain event occurs.
  • Generic Host call StopAsync stop.

.NET Core 3.0 currently there are three different IHostLifetime achieve:

  • ConsoleLifetime - listening SIGTERM or Ctrl + C and stop the host application.
  • SystemdLifetime - SIGTERM monitor and stop the host application, and notify the relevant systemd status changes ( "Ready" and "Stopping")
  • Windows ServiceLifetime - hook event for Windows Service Lifecycle Management

By default, the use of generic host ConsoleLifetime, it offers you the usual behavior in ASP.NET Core 2.x, when the application is received from the console to SIGTERM signal or Ctrl + C, the application will stop. When you create a Worker Service (Windows or systemd services), mainly IHostLifetime configuration for the application.

 

Third, start to understand the application

When I was studying this new abstraction, he began to feel very confused. When it will be called? It has to do with ApplicationLifetime? Who first called IHostLifetime? To make things clearer, I took some time to find the default ASP.NET Core 3.0 interactions between their applications.

In this article, we start from the default ASP.NET Core 3.0 Program.cs file.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

In particular, once constructed generic Host objects, I will Run () function calls of interest.

Please note that I will not be a detailed description of the code - I think I will skip any irrelevant content. My goal is to have a whole interactive feel. If you want to go a little deeper, you can view the source code!

Run () method is HostingAbstractionsHostExtensions extension, it calls RunAsync () and blocks until the method exits. When this method exits, the application exits, so all the interesting things are happening there! The diagram below summarizes RunAsync occurs in (), will be discussed in details below:

 

 

 (Picture from: https://andrewlock.net/introducing-ihostlifetime-and-untangling-the-generic-host-startup-interactions/ )

Program.cs call Run () extension method, which calls RunAsync () extension method. In turn call StartAsync on IHost instance (). StartAsync ways to accomplish such as start IHostingServices (will be described later) like a lot of work, but this method will return soon after being called.

Next, RunAsync () method to call another extension method, called WaitForShutdownAsync (). This extension method all other operations shown in FIG. The name is very descriptive. This method is its own configuration, so that it is suspended until the trigger ApplicationStopping cancel token on IHostApplicationLifetime far (we will soon learn how to trigger the token).

Extension Method WaitForShutdownAsync () using the associated Task TaskCompletionSource and wait for this purpose. This is not what I need to use the previous model, it looks very interesting, so I added it below (adapted from HostingAbstractionsHostExtensions)

public static async Task WaitForShutdownAsync(this IHost host)
{
    // Get the lifetime object from the DI container
    var applicationLifetime = host.Services.GetService<IHostApplicationLifetime>();

    // Create a new TaskCompletionSource called waitForStop
    var waitForStop = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);

    // Register a callback with the ApplicationStopping cancellation token
    applicationLifetime.ApplicationStopping.Register(obj =>
    {
        var tcs = (TaskCompletionSource<object>)obj;

        // When the application stopping event is fired, set 
        // the result for the waitForStop task, completing it
        tcs.TrySetResult(null);
    }, waitForStop);

    // Await the Task. This will block until ApplicationStopping is triggered,
    // and TrySetResult(null) is called
    await waitForStop.Task;

    // We're shutting down, so call StopAsync on IHost
    await host.StopAsync();
}

This extension application method explains how to "pause" in the operating state, and everything runs in the background task. Let us better understand the IHost.StartAsync the top of the map () method call.

 

四、Host.StartAsync()

In the figure above, we studied HostingAbstractionsHostExtensions extension method that runs on an interface IHost. If we want to know what usually happens when you call IHost.StartAsync (), then we need to see a specific implementation. The following figure shows the general Host actual implementation startAsync () method. Similarly, we take a look at the interesting part.

 

 

 You can see from the chart, there's a lot of steps! Call Host.StartAsync () is obtained by calling WaitForStartAsync () on IHostLifetime we described earlier in this example starting. At this behavior depends on what you are using IHostLifetime, but I will assume that we are using ConsoleLifetime (the default setting ASP.NET Core application) for this article.

 

Note: SystemdLifetime ConsoleLifetime behaves very similar and has some additional features. WindowsServiceLifetime is completely different, and derived from System.ServiceProcess.ServiceBase.

ConsoleLifetime.WaitForStartAsync () method (shown below) to do one important thing: it adds an event listener for the SIGTERM request console and Ctrl + C. These events will trigger a request to close the application. Therefore, usually the responsibility of the IHostLifetime control when the application is closed.

public Task WaitForStartAsync(CancellationToken cancellationToken)
{
    // ... logging removed for brevity

    // Attach event handlers for SIGTERM and Ctrl+C
    AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
    Console.CancelKeyPress += OnCancelKeyPress;

    // Console applications start immediately.
    return Task.CompletedTask;
}

 

As shown in the code above, this method is now complete, and returns control to the Host.StartAsync (). At this time, the host loads all IHostedService instances and call startAsync () in each instance. This includes GenericWebHostService for starting Kestrel Web server (the server last start).

Once all IHostedServices started, Host.StartAsync () is called IHostApplicationLifetime.NotifyStarted () to trigger all callbacks registered (usually just record) and exit.

Please note, IhostLifetime and IHostApplicationLifetime different. The former contains the logic for controlling application startup time. The latter (implemented by ApplicationLifetime) contains CancellationTokens, you can register a callback to run applications at various time points according to their life cycle.

 

In this case, the application is in "Run" state, all background services are running, Kestrel process the request, and the original WaitForShutdownAsync () extension method waits ApplicationStopping event is triggered. Finally, let's look at what happens typing Ctrl + C while in the console.

 

五、shutdown process

When ConsoleLifetime SIGTERM signal received from the console or the Ctrl + C (cancel button), the closing process occurs. The following figure shows the interaction between all the key participants in the closing process:

 

 (1) When the trigger Ctrl + C to terminate the event, ConsoleLifetime calls IHostApplicationLifetime.StopApplication () method. This will trigger a callback using ApplicationStopping cancel all registered token. If you look back at the program overview, you will see that the trigger is the original RunAsync () extension method is waiting for a trigger, so wait for the task is completed, and calls Host.StopAsync ().

(2) Host.StopAsync () call IHostApplicationLifetime.StopApplication start again () through. The second call is noop second run, but this is necessary because technically, there are other ways to trigger Host.StopAsync () of.

(3) Next, the host in the reverse order to close all IHostedServices. First, start the service will finally stop, so GenericWebHostedService the first to be closed.

(4)关闭服务后,将调用IHostLifetime.StopAsync,它对于ConsoleLifetime是noop(空操作)。 最后,Host.StopAsync()在退出之前调用IHostApplicationLifetime.NotifyStopped()以通知任何关联的处理程序。

(5)此时,一切都关闭,Program.Main函数退出,应用程序退出。

 

六、总结

在这篇文章中,聊了一些有关如何在通用主机之上重新构建ASP.NET Core 3.0的背景,并介绍了新的IHostLifetime接口。 然后,我详细描述了使用通用主机的典型ASP.NET Core 3.0应用程序的启动和关闭所涉及的各种类和接口之间的交互。如果还是不明白的同学可以查看源码,希望它对你有所帮助!

 

 

翻译:Andrew Lock   https://andrewlock.net/introducing-ihostlifetime-and-untangling-the-generic-host-startup-interactions/

 

作者:郭峥

出处:http://www.cnblogs.com/runningsmallguo/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

Guess you like

Origin www.cnblogs.com/runningsmallguo/p/11617246.html