Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit editor mode

.NET Generic Host

Feedback

In this article

In this article, you learn about the various patterns for configuring and building a .NET Generic Host available in theMicrosoft.Extensions.Hosting NuGet package. The .NET Generic Host is responsible for app startup and lifetime management. The Worker Service templates create a .NET Generic Host,HostApplicationBuilder. The Generic Host can be used with other types of .NET applications, such as Console apps.

Ahost is an object that encapsulates an app's resources and lifetime functionality, such as:

  • Dependency injection (DI)
  • Logging
  • Configuration
  • App shutdown
  • IHostedService implementations

When a host starts, it callsIHostedService.StartAsync on each implementation ofIHostedService registered in the service container's collection of hosted services. In a worker service app, allIHostedService implementations that containBackgroundService instances have theirBackgroundService.ExecuteAsync methods called.

The main reason for including all of the app's interdependent resources in one object is lifetime management: control over app startup and graceful shutdown.

Set up a host

The host is typically configured, built, and run by code in theProgram class. TheMain method:

The .NET Worker Service templates generate the following code to create a Generic Host:

using Example.WorkerService;HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Services.AddHostedService<Worker>();IHost host = builder.Build();host.Run();

For more information on Worker Services, seeWorker Services in .NET.

Host builder settings

TheCreateApplicationBuilder method:

  • Sets the content root to the path returned byGetCurrentDirectory().
  • Loadshost configuration from:
    • Environment variables prefixed withDOTNET_.
    • Command-line arguments.
  • Loads app configuration from:
    • appsettings.json.
    • appsettings.{Environment}.json.
    • Secret Manager when the app runs in theDevelopment environment.
    • Environment variables.
    • Command-line arguments.
  • Adds the following logging providers:
    • Console
    • Debug
    • EventSource
    • EventLog (only when running on Windows)
  • Enables scope validation anddependency validation when the environment isDevelopment.

TheHostApplicationBuilder.Services is anMicrosoft.Extensions.DependencyInjection.IServiceCollection instance. These services are used to build anIServiceProvider that's used with dependency injection to resolve the registered services.

Framework-provided services

When you call eitherIHostBuilder.Build() orHostApplicationBuilder.Build(), the following services are registered automatically:

Additional scenario-based host builders

If you're building for the web or writing a distributed application, you might need to use a different host builder. Consider the following list of additional host builders:

IHostApplicationLifetime

Inject theIHostApplicationLifetime service into any class to handle post-startup and graceful shutdown tasks. Three properties on the interface are cancellation tokens used to register app start and app stop event handler methods. The interface also includes aStopApplication() method.

The following example is anIHostedService andIHostedLifecycleService implementation that registersIHostApplicationLifetime events:

using Microsoft.Extensions.Hosting;using Microsoft.Extensions.Logging;namespace AppLifetime.Example;public sealed class ExampleHostedService : IHostedService, IHostedLifecycleService{    private readonly ILogger _logger;    public ExampleHostedService(        ILogger<ExampleHostedService> logger,        IHostApplicationLifetime appLifetime)    {        _logger = logger;        appLifetime.ApplicationStarted.Register(OnStarted);        appLifetime.ApplicationStopping.Register(OnStopping);        appLifetime.ApplicationStopped.Register(OnStopped);    }    Task IHostedLifecycleService.StartingAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("1. StartingAsync has been called.");        return Task.CompletedTask;    }    Task IHostedService.StartAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("2. StartAsync has been called.");        return Task.CompletedTask;    }    Task IHostedLifecycleService.StartedAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("3. StartedAsync has been called.");        return Task.CompletedTask;    }    private void OnStarted()    {        _logger.LogInformation("4. OnStarted has been called.");    }    private void OnStopping()    {        _logger.LogInformation("5. OnStopping has been called.");    }    Task IHostedLifecycleService.StoppingAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("6. StoppingAsync has been called.");        return Task.CompletedTask;    }    Task IHostedService.StopAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("7. StopAsync has been called.");        return Task.CompletedTask;    }    Task IHostedLifecycleService.StoppedAsync(CancellationToken cancellationToken)    {        _logger.LogInformation("8. StoppedAsync has been called.");        return Task.CompletedTask;    }    private void OnStopped()    {        _logger.LogInformation("9. OnStopped has been called.");    }}

The Worker Service template could be modified to add theExampleHostedService implementation:

using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using AppLifetime.Example;HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Services.AddHostedService<ExampleHostedService>();using IHost host = builder.Build();await host.RunAsync();

The application would write the following sample output:

// Sample output://     info: AppLifetime.Example.ExampleHostedService[0]//           1.StartingAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           2.StartAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           3.StartedAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           4.OnStarted has been called.//     info: Microsoft.Hosting.Lifetime[0]//           Application started. Press Ctrl+C to shut down.//     info: Microsoft.Hosting.Lifetime[0]//           Hosting environment: Production//     info: Microsoft.Hosting.Lifetime[0]//           Content root path: ..\app-lifetime\bin\Debug\net8.0//     info: AppLifetime.Example.ExampleHostedService[0]//           5.OnStopping has been called.//     info: Microsoft.Hosting.Lifetime[0]//           Application is shutting down...//     info: AppLifetime.Example.ExampleHostedService[0]//           6.StoppingAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           7.StopAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           8.StoppedAsync has been called.//     info: AppLifetime.Example.ExampleHostedService[0]//           9.OnStopped has been called.

The output shows the order of all of the various lifecycle events:

  1. IHostedLifecycleService.StartingAsync
  2. IHostedService.StartAsync
  3. IHostedLifecycleService.StartedAsync
  4. IHostApplicationLifetime.ApplicationStarted

When the application is stopped, for example withCtrl+C, the following events are raised:

  1. IHostApplicationLifetime.ApplicationStopping
  2. IHostedLifecycleService.StoppingAsync
  3. IHostedService.StopAsync
  4. IHostedLifecycleService.StoppedAsync
  5. IHostApplicationLifetime.ApplicationStopped

IHostLifetime

TheIHostLifetime implementation controls when the host starts and when it stops. The last implementation registered is used.Microsoft.Extensions.Hosting.Internal.ConsoleLifetime is the defaultIHostLifetime implementation. For more information on the lifetime mechanics of shutdown, seeHost shutdown.

TheIHostLifetime interface exposes aIHostLifetime.WaitForStartAsync method, which is called at the start ofIHost.StartAsync which will wait until it's complete before continuing. This can be used to delay startup until signaled by an external event.

Additionally, theIHostLifetime interface exposes aIHostLifetime.StopAsync method, which is called fromIHost.StopAsync to indicate that the host is stopping and it's time to shut down.

IHostEnvironment

Inject theIHostEnvironment service into a class to get information about the following settings:

Additionally, theIHostEnvironment service exposes the ability to evaluate the environment with the help of these extension methods:

Host configuration

Host configuration is used to configure properties of theIHostEnvironment implementation.

The host configuration is available inHostApplicationBuilderSettings.Configuration property and the environment implementation is available inIHostApplicationBuilder.Environment property. To configure the host, access theConfiguration property and call any of the available extension methods.

To add host configuration, consider the following example:

using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Hosting;HostApplicationBuilderSettings settings = new(){    Args = args,    Configuration = new ConfigurationManager(),    ContentRootPath = Directory.GetCurrentDirectory(),};settings.Configuration.AddJsonFile("hostsettings.json", optional: true);settings.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");settings.Configuration.AddCommandLine(args);HostApplicationBuilder builder = Host.CreateApplicationBuilder(settings);using IHost host = builder.Build();// Application code should start here.await host.RunAsync();

The preceding code:

  • Sets the content root to the path returned byGetCurrentDirectory().
  • Loads host configuration from:
    • hostsettings.json.
    • Environment variables prefixed withPREFIX_.
    • Command-line arguments.

App configuration

App configuration is created by callingConfigureAppConfiguration on anIHostApplicationBuilder. The publicIHostApplicationBuilder.Configuration property allows consumers to read from or make changes to the existing configuration using available extension methods.

For more information, seeConfiguration in .NET.

Host shutdown

There are several ways in which a hosted process is stopped. Most commonly, a hosted process can be stopped in the following ways:

The hosting code isn't responsible for handling these scenarios. The owner of the process needs to deal with them the same as any other app. There are several other ways in which a hosted service process can be stopped:

  • IfConsoleLifetime is used (UseConsoleLifetime), it listens for the following signals and attempts to stop the host gracefully.
    • SIGINT (orCtrl+C).
    • SIGQUIT (orCtrl+BREAK on Windows,Ctrl+\ on Unix).
    • SIGTERM (sent by other apps, such asdocker stop).
  • If the app callsEnvironment.Exit.

The built-in hosting logic handles these scenarios, specifically theConsoleLifetimeclass.ConsoleLifetime tries to handle the "shutdown" signals SIGINT, SIGQUIT, and SIGTERM to allow for a graceful exit to theapplication.

Before .NET 6, there wasn't a way for .NET code to gracefully handle SIGTERM. To work around this limitation,ConsoleLifetime would subscribe toSystem.AppDomain.ProcessExit. WhenProcessExit was raised,ConsoleLifetime would signal the host to stop and block theProcessExit thread, waiting for the host to stop.

The process exit handler would allow for the clean-up code in the application to run—for example,IHost.StopAsync and code afterHostingAbstractionsHostExtensions.Run in theMain method.

However, there were other issues with this approach because SIGTERM wasn't the only wayProcessExit was raised. SIGTERM is also raised when app code callsEnvironment.Exit.Environment.Exit isn't a graceful way of shutting down a process in theMicrosoft.Extensions.Hosting app model. It raises theProcessExit event and then exits the process. The end of theMain method doesn't get executed. Background and foreground threads are terminated, andfinally blocksaren't executed.

SinceConsoleLifetime blockedProcessExit while waiting for the host to shut down, this behavior led todeadlocks fromEnvironment.Exit also blocks waiting for the call toProcessExit. Additionally, since the SIGTERM handling was attempting to gracefully shut down the process,ConsoleLifetime would set theExitCode to0, whichclobbered the user's exit code passed toEnvironment.Exit.

In .NET 6,POSIX signals are supported and handled. TheConsoleLifetime handles SIGTERM gracefully, and no longer gets involved whenEnvironment.Exit is invoked.

Tip

For .NET 6+,ConsoleLifetime no longer has logic to handle scenarioEnvironment.Exit. Apps that callEnvironment.Exit and need to perform clean-up logic can subscribe toProcessExit themselves. Hosting will no longer attempt to gracefully stop the host in these scenarios.

If your application uses hosting, and you want to gracefully stop the host, you can callIHostApplicationLifetime.StopApplication instead ofEnvironment.Exit.

Hosting shutdown process

The following sequence diagram shows how the signals are handled internally in the hosting code. Most users don't need to understand this process. But for developers that need a deep understanding, a good visual may help you get started.

After the host has been started, when a user callsRun orWaitForShutdown, a handler gets registered forIApplicationLifetime.ApplicationStopping. Execution is paused inWaitForShutdown, waiting for theApplicationStopping event to be raised. TheMain method doesn't return right away, and the app stays running untilRun orWaitForShutdown returns.

When a signal is sent to the process, it initiates the following sequence:

Hosting shutdown sequence diagram.

  1. The control flows fromConsoleLifetime toApplicationLifetime to raise theApplicationStopping event. This signalsWaitForShutdownAsync to unblock theMain execution code. In the meantime, the POSIX signal handler returns withCancel = true since the POSIX signal has been handled.
  2. TheMain execution code starts executing again and tells the host toStopAsync(), which in turn stops all the hostedservices, and raises any other stopped events.
  3. Finally,WaitForShutdown exits, allowing for any application clean-up code to execute, and for theMain methodto exit gracefully.

Host shutdown in web server scenarios

There are various other common scenarios in which graceful shutdown works in Kestrel for both HTTP/1.1 and HTTP/2 protocols, and how you can configure it in different environments with a load balancer to drain traffic smoothly. While web server configuration is beyond the scope of this article, you can find more information onConfigure options for the ASP.NET Core Kestrel web server documentation.

When the Host receives a shutdown signal (for example,Ctrl+C orStopAsync), it notifies the application by signalingApplicationStopping. You should subscribe to this event if you have any long-running operations that need to finish gracefully.

Next, the Host callsIServer.StopAsync with a shutdown timeout that you can configure (default 30s). Kestrel (and Http.Sys) close their port bindings and stop accepting new connections. They also tell the current connections to stop processing new requests. For HTTP/2 and HTTP/3, a preliminaryGOAWAY message is sent to the client. For HTTP/1.1, they stop the connection loop because requests are processed in order. IIS behaves differently, by rejecting new requests with a 503 status code.

The active requests have until the shutdown timeout to complete. If they're all complete before the timeout, the server returns control to the host sooner. If the timeout expires, the pending connections and requests are aborted forcefully, which can cause errors in the logs and to the clients.

Load balancer considerations

To ensure a smooth transition of clients to a new destination when working with a load balancer, you can follow these steps:

  • Bring up the new instance and start balancing traffic to it (you may already have several instances for scaling purposes).
  • Disable or remove the old instance in the load balancer configuration so it stops receiving new traffic.
  • Signal the old instance to shut down.
  • Wait for it to drain or time out.

See also

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNoNo

Need help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?

  • Last updated on

In this article

Was this page helpful?

YesNo
NoNeed help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?