Graceful shutdown with Generic Host in .NET Core 2.1

前端 未结 3 1339
时光取名叫无心
时光取名叫无心 2020-12-05 15:17

.NET Core 2.1 introduced new Generic Host, which allows to host non-HTTP workloads with all benefits of Web Host. Currently, there is no much information and recipes with it

相关标签:
3条回答
  • 2020-12-05 15:34

    I'll share some patterns I think works very well for non-WebHost projects.

    namespace MyNamespace
    {
        public class MyService : BackgroundService
        {
            private readonly IServiceProvider _serviceProvider;
            private readonly IApplicationLifetime _appLifetime;
    
            public QbdSkuVaultIntSchedulerService(
                IServiceProvider serviceProvider,
                IApplicationLifetime appLifetime)
            {
                _serviceProvider = serviceProvider;
                _appLifetime = appLifetime;
            }
    
            protected override Task ExecuteAsync(CancellationToken stoppingToken)
            {
                _appLifetime.ApplicationStopped.Register(OnStopped);
    
                return RunAsync(stoppingToken);
            }
    
            private async Task RunAsync(CancellationToken token)
            {
                while (!token.IsCancellationRequested)
                {
                    using (var scope = _serviceProvider.CreateScope())
                    {
                        var runner = scope.ServiceProvider.GetRequiredService<IMyJobRunner>();
                        await runner.RunAsync();
                    }
                }
            }
    
            public void OnStopped()
            {
                Log.Information("Window will close automatically in 20 seconds.");
                Task.Delay(20000).GetAwaiter().GetResult();
            }
        }
    }
    

    A couple notes about this class:

    1. I'm using the BackgroundService abstract class to represent my service. It's available in the Microsoft.Extensions.Hosting.Abstractions package. I believe this is planned to be in .NET Core 3.0 out of the box.
    2. The ExecuteAsync method needs to return a Task representing the running service. Note: If you have a synchronous service wrap your "Run" method in Task.Run().
    3. If you want to do additional setup or teardown for your service you can inject the app lifetime service and hook into events. I added an event to be fired after the service is fully stopped.
    4. Because you don't have the auto-magic of new scope creation for each web request as you do in MVC projects you have to create your own scope for scoped services. Inject IServiceProvider into the service to do that. All dependencies on the scope should be added to the DI container using AddScoped().

    Set up the host in Main( string[] args ) so that it shuts down gracefully when CTRL+C / SIGTERM is called:

    IHost host = new HostBuilder()
        .ConfigureServices( ( hostContext, services ) =>
        {
            services.AddHostedService<MyService>();
        })
        .UseConsoleLifetime()
        .Build();
    
    host.Run();  // use RunAsync() if you have access to async Main()
    

    I've found this set of patterns to work very well outside of ASP.NET applications.

    Be aware that Microsoft has built against .NET Standard so you don't need to be on .NET Core to take advantage of these new conveniences. If you're working in Framework just add the relevant NuGet packages. The package is built against .NET Standard 2.0 so you need to be on Framework 4.6.1 or above. You can find the code for all of the infrastructure here and feel free to poke around at the implementations for all the abstractions you are working with: https://github.com/aspnet/Extensions

    0 讨论(0)
  • 2020-12-05 15:35

    You need IApplicationLifetime. This provides you with all the needed information about application start and shutdown. You can even trigger the shutdown with it via appLifetime.StopApplication();

    Look at https://github.com/aspnet/Docs/blob/66916c2ed3874ed9b000dfd1cab53ef68e84a0f7/aspnetcore/fundamentals/host/generic-host/samples/2.x/GenericHostSample/LifetimeEventsHostedService.cs

    Snippet(if the link becomes invalid):

    public Task StartAsync(CancellationToken cancellationToken)
    {
        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);
    
        return Task.CompletedTask;
    }
    
    0 讨论(0)
  • 2020-12-05 15:43

    In Startup.cs, you can terminate the application with the Kill() method of the current process:

            public void Configure(IHostApplicationLifetime appLifetime)
            {
                appLifetime.ApplicationStarted.Register(() =>
                {
                    Console.WriteLine("Press Ctrl+C to shut down.");
                });
    
                appLifetime.ApplicationStopped.Register(() =>
                {
                    Console.WriteLine("Shutting down...");
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
                });
            }
    

    Program.cs

    Don't forget to use UseConsoleLifetime() while building the host.

    Host.CreateDefaultBuilder(args).UseConsoleLifetime(opts => opts.SuppressStatusMessages = true);
    
    0 讨论(0)
提交回复
热议问题