Host genérico de .NET
Las plantillas de servicio de trabajo crean un host genérico de .NET, HostBuilder. El host genérico se puede usar con otros tipos de aplicaciones .NET, como aplicaciones de consola.
El host es un objeto que encapsula los recursos y la funcionalidad de vigencia de una aplicación, como:
- Inserción de dependencias (ID)
- Registro
- Configuración
- Apagado de la aplicación
- Implementaciones de
IHostedService
Cuando se inicia un host, llama a IHostedService.StartAsync en cada implementación de IHostedService registrada en la colección de servicios hospedados del contenedor de servicios. En una aplicación de servicio de trabajo, todas las implementaciones de IHostedService
que contienen instancias de BackgroundService tienen los métodos BackgroundService.ExecuteAsync a los que se llama.
La razón principal para incluir todos los recursos interdependientes de la aplicación en un objeto es la administración de la duración: el control sobre el inicio de la aplicación y el apagado estable. Esto se logra con el paquete NuGet Microsoft.Extensions.Hosting.
Configuración de un host
Normalmente se configura, compila y ejecuta el host por el código de la clase Program
. El método Main
realiza las acciones siguientes:
- Llama a un método CreateDefaultBuilder() para crear y configurar un objeto del generador.
- Llama a Build() para crear una instancia de IHost.
- Llama al método Run o RunAsync en el objeto host.
Las plantillas de servicio de trabajo de .NET generan el código siguiente para crear un host genérico:
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
})
.Build();
host.Run();
Configuración predeterminada del generador
El método CreateDefaultBuilder realiza las acciones siguientes:
- Establece la raíz de contenido en la ruta de acceso devuelta por GetCurrentDirectory().
- Carga la configuración de host de:
- Variables de entorno con el prefijo
DOTNET_
. - Argumentos de la línea de comandos.
- Variables de entorno con el prefijo
- Carga la configuración de aplicación de:
- appsettings.json.
- appsettings.{Environment}.json.
- Administrador de secretos, cuando la aplicación se ejecuta en el entorno
Development
. - Variables de entorno.
- Argumentos de la línea de comandos.
- Agrega los siguientes proveedores de registro:
- Consola
- Depuración
- EventSource
- EventLog (solo si se ejecuta en Windows)
- Permite la validación del ámbito y la validación de dependencias si el entorno es
Development
.
El método ConfigureServices
expone la capacidad de agregar servicios a la instancia de Microsoft.Extensions.DependencyInjection.IServiceCollection. Más adelante, estos servicios se pueden poner a disposición de la inserción de dependencias.
Servicios proporcionados por el marco de trabajo
Los servicios siguientes se registran de forma automática:
IHostApplicationLifetime
Permite insertar el servicio IHostApplicationLifetime en cualquier clase para controlar las tareas posteriores al inicio y el cierre estable. Tres de las propiedades de la interfaz son tokens de cancelación que se usan para registrar los métodos del controlador de eventos de inicio y detención de las aplicaciones. La interfaz también incluye un método StopApplication().
El ejemplo siguiente es una implementación de IHostedService
que registra los eventos IHostApplicationLifetime
:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace AppLifetime.Example;
public sealed class ExampleHostedService : IHostedService
{
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);
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("1. StartAsync has been called.");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("4. StopAsync has been called.");
return Task.CompletedTask;
}
private void OnStarted()
{
_logger.LogInformation("2. OnStarted has been called.");
}
private void OnStopping()
{
_logger.LogInformation("3. OnStopping has been called.");
}
private void OnStopped()
{
_logger.LogInformation("5. OnStopped has been called.");
}
}
La plantilla de servicio de trabajo se puede modificar para agregar la implementación de ExampleHostedService
:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppLifetime.Example;
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddHostedService<ExampleHostedService>())
.Build();
await host.RunAsync();
La aplicación escribiría la siguiente salida de ejemplo:
// Sample output:
// info: ExampleHostedService[0]
// 1. StartAsync has been called.
// info: ExampleHostedService[0]
// 2. 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\net6.0
// info: ExampleHostedService[0]
// 3. OnStopping has been called.
// info: Microsoft.Hosting.Lifetime[0]
// Application is shutting down...
// info: ExampleHostedService[0]
// 4. StopAsync has been called.
// info: ExampleHostedService[0]
// 5. OnStopped has been called.
IHostLifetime
La implementación de IHostLifetime controla cuándo se inicia el host y cuándo se detiene. Se usa la última implementación registrada. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime
es la implementación predeterminada de IHostLifetime
. Para obtener más información sobre la mecánica de vigencia del apagado, consulte Apagado del host.
IHostEnvironment
Permite insertar el servicio IHostEnvironment en una clase para obtener información sobre los valores siguientes:
- IHostEnvironment.ApplicationName
- IHostEnvironment.ContentRootFileProvider
- IHostEnvironment.ContentRootPath
- IHostEnvironment.EnvironmentName
Configuración de host
La configuración de host se usa para configurar las propiedades de la implementación de IHostEnvironment.
La configuración de host está disponible en HostBuilderContext.Configuration dentro del método ConfigureAppConfiguration. Al llamar al método ConfigureAppConfiguration
, HostBuilderContext
y IConfigurationBuilder
se pasan a configureDelegate
. configureDelegate
se define como Action<HostBuilderContext, IConfigurationBuilder>
. El contexto del generador de hosts expone la propiedad Configuration
, que es una instancia de IConfiguration
. Representa la configuración generada a partir del host, mientras que IConfigurationBuilder
es el objeto de generador que se usa para configurar la aplicación.
Sugerencia
Después de llamar a ConfigureAppConfiguration
, HostBuilderContext.Configuration
se reemplaza por la configuración de la aplicación.
Para agregar la configuración de host, llame a ConfigureHostConfiguration en IHostBuilder
. Se puede llamar varias veces a ConfigureHostConfiguration
con resultados de suma. El host usa cualquier opción que establezca un valor en último lugar en una clave determinada.
En el ejemplo siguiente se crea la configuración de host:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddJsonFile("hostsettings.json", optional: true);
configHost.AddEnvironmentVariables(prefix: "PREFIX_");
configHost.AddCommandLine(args);
})
.Build();
// Application code should start here.
await host.RunAsync();
Configuración de aplicaciones
La configuración de la aplicación se crea llamando a ConfigureAppConfiguration en IHostBuilder
. Se puede llamar varias veces a ConfigureAppConfiguration
con resultados de suma. La aplicación usa cualquier opción que establezca un valor en último lugar en una clave determinada.
La configuración creada por ConfigureAppConfiguration
está disponible en HostBuilderContext.Configuration para las operaciones posteriores y como servicio de ID. La configuración de host también se agrega a la configuración de la aplicación.
Para obtener más información, vea Configuración en .NET.
Apagado del host
Un proceso de servicio hospedado se puede detener de las maneras siguientes:
- Si alguien no llama a Run ni HostingAbstractionsHostExtensions.WaitForShutdown, y la aplicación se cierra normalmente luego de que
Main
finaliza. - Si la aplicación se bloquea.
- Si se fuerza el cierre de la aplicación mediante SIGKILL (o CTRL+Z).
Todos estos escenarios no se controlan directamente mediante el código de hospedaje. El propietario del proceso debe tratar con ellos igual que cualquier aplicación. Hay varias maneras adicionales en las que se puede detener un proceso de servicio hospedado:
- Si se usa
ConsoleLifetime
, escucha las siguientes señales e intenta detener el host correctamente. - Si la aplicación llama a Environment.Exit.
Estos escenarios se controlan mediante la lógica de hospedaje integrada, específicamente la clase ConsoleLifetime
. ConsoleLifetime
intenta controlar las señales de "apagado" SIGINT, SIGQUIT y SIGTERM para permitir una salida correcta de la aplicación.
Antes de .NET 6, no había ninguna manera de que el código de .NET controlara correctamente SIGTERM. Para superar esta limitación, ConsoleLifetime
se suscribiría a System.AppDomain.ProcessExit. Al generar ProcessExit
, ConsoleLifetime
señalaría al host que detenga y bloquee el subproceso ProcessExit
, esperando a que el host se detenga.
Esto permitiría que el código de limpieza de la aplicación se ejecutara; por ejemplo, IHost.StopAsync y el código después de HostingAbstractionsHostExtensions.Run en el método Main
.
Esto produjo otros problemas, ya que SIGTERM no fue la única manera en que ProcessExit
se generó. También se genera mediante código en la aplicación que llama a Environment.Exit
. Environment.Exit
no es una manera correcta de cerrar un proceso en el modelo de aplicación Microsoft.Extensions.Hosting
. Genera el evento ProcessExit
y, a continuación, sale del proceso. El final del método Main
no se ejecuta. Los subprocesos en segundo plano y en primer plano finalizan, y los bloques finally
no se ejecutan.
Puesto que ConsoleLifetime
bloqueaba a ProcessExit
mientras esperaba a que el host se apagase, este comportamiento provocaba interbloqueos de Environment.Exit
y bloqueos a la espera de la llamada a ProcessExit
. Además, dado que el control SIGTERM estaba intentando cerrar el proceso correctamente, ConsoleLifetime
establecería ExitCode en 0
, lo que obstruyó el código de salida del usuario que se pasó a Environment.Exit
.
En .NET 6, se admiten y controlan las señales POSIX. Esto permite que ConsoleLifetime
controle SIGTERM correctamente y ya no se involucre cuando se invoque a Environment.Exit
.
Sugerencia
Para .NET 6+, ConsoleLifetime
ya no tiene lógica para controlar el escenario Environment.Exit
. Las aplicaciones que llaman a Environment.Exit
y necesitan realizar una lógica de limpieza pueden suscribirse automáticamente a ProcessExit
. El hospedaje ya no intentará detener correctamente el host en este escenario.
Si la aplicación usa hospedaje, y usted quiere detener correctamente el host, puede llamar a IHostApplicationLifetime.StopApplication en lugar de a Environment.Exit
.
Proceso de apagado del hospedaje
En el siguiente diagrama de secuencia se muestra cómo se controlan internamente las señales en el código de hospedaje. La mayoría de los usuarios no necesita comprender este proceso. Pero para los desarrolladores que necesitan un conocimiento profundo, esto puede resultar de ayuda para empezar.
Una vez iniciado el host, cuando un usuario llama a Run
o WaitForShutdown
, se registra un controlador para IApplicationLifetime.ApplicationStopping. La ejecución se pausa en WaitForShutdown
, a la espera de que se pueda generar el evento ApplicationStopping
. Así es como el método Main
no se devuelve de inmediato, y la aplicación permanece en ejecución hasta que se devuelve Run
o WaitForShutdown
.
Cuando se envía una señal al proceso, inicia la secuencia siguiente:
- El control fluye de
ConsoleLifetime
aApplicationLifetime
para generar el eventoApplicationStopping
. Esto indica queWaitForShutdownAsync
desbloquee el código de ejecución deMain
. Mientras tanto, el controlador de señal POSIX se devuelve conCancel = true
, ya que se ha controlado esta señal POSIX. - El código de ejecución de
Main
comienza a ejecutarse de nuevo e indica al host queStopAsync()
, lo que, a su vez, detiene todos los servicios hospedados y genera cualquier otro evento detenido. - Por último,
WaitForShutdown
se cierra, lo que permite que cualquier aplicación limpie el código que se va a ejecutar y que el métodoMain
salga correctamente.
Vea también
- Inserción de dependencias en .NET
- Registro en .NET
- Configuración en .NET
- Servicios Worker en .NET
- Host web de ASP.NET Core
- Se deben crear errores de host genéricos en el repositorio de github.com/dotnet/runtime