Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Sugerencia
Este contenido es un extracto del libro electrónico, ".NET Microservices Architecture for Containerized .NET Applications" (Arquitectura de microservicios de .NET para aplicaciones de .NET contenedorizadas), disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.
Las tareas en segundo plano y los trabajos programados son algo que podría necesitar usar en cualquier aplicación, independientemente de si sigue o no el patrón de arquitectura de microservicios. La diferencia al usar una arquitectura de microservicios es que puede implementar la tarea en segundo plano en un proceso o contenedor independiente para hospedarlo y poder escalarlo hacia abajo o hacia arriba según sus necesidades.
Desde un punto de vista genérico, en .NET llamamos a este tipo de tareas Servicios hospedados, ya que son servicios o lógicas que hospeda dentro del host, la aplicación o el microservicio. Tenga en cuenta que, en este caso, el servicio hospedado simplemente significa una clase con la lógica de tareas en segundo plano.
Desde .NET Core 2.0, el marco proporciona una nueva interfaz denominada IHostedService que le ayuda a implementar fácilmente los servicios hospedados. La idea básica es que puede registrar varias tareas de fondo (servicios hospedados) que se ejecutan mientras su servidor web o host está en funcionamiento, según se muestra en la imagen 6-26.
Figura 6-26. Uso de IHostedService en un WebHost frente a un host
ASP.NET Core 1.x y 2.x ofrecen compatibilidad IWebHost para procesos en segundo plano en aplicaciones web. .NET Core 2.1 y versiones posteriores admiten IHost para procesos en segundo plano con aplicaciones de consola simples. Observe la diferencia entre WebHost y Host.
Una clase base WebHost (que implementa IWebHost) en ASP.NET Core 2.0 es el artefacto de infraestructura que se usa para proporcionar características del servidor HTTP al proceso, como al implementar una aplicación web MVC o un servicio de API web. Proporciona todas las ventajas de la nueva infraestructura de ASP.NET Core, lo que le permite usar la inserción de dependencias e insertar middleware en la canalización de solicitudes, así como actividades similares. El WebHost usa estos mismos IHostedServices para las tareas en segundo plano.
Se introdujo una Host clase base que implementa IHosten .NET Core 2.1. Básicamente, Host permite tener una infraestructura similar a la que tiene con WebHost (inyección de dependencias, servicios hospedados, etc.), pero en este caso, solo quiere tener un proceso simple y más ligero como host, sin nada relacionado con las características de MVC, Web API o servidor HTTP.
Por lo tanto, puede optar por crear un proceso de host especializado con IHost para controlar los servicios hospedados y nada más, como un microservicio dedicado a hospedar exclusivamente IHostedServices. Alternativamente, puede ampliar una aplicación ASP.NET Core WebHost existente, tal como una aplicación Web API o MVC de ASP.NET Core existente.
Cada enfoque tiene ventajas y desventajas en función de sus necesidades empresariales y de escalabilidad. Lo fundamental es básicamente que si las tareas en segundo plano no tienen nada que ver con HTTP (IWebHost), usted debe usar IHost.
Registro de servicios hospedados en Host o WebHost
Vamos a profundizar más en la IHostedService interfaz, ya que su uso es bastante similar en un WebHost o en un Host.
SignalR es un ejemplo de un artefacto con servicios hospedados, pero también puede utilizarlo para cosas mucho más sencillas como las siguientes:
- Tarea en segundo plano que sondea una base de datos que busca cambios.
- Una tarea programada que actualiza algunas memorias caché periódicamente.
- Implementación de QueueBackgroundWorkItem que permite ejecutar una tarea en un subproceso en segundo plano.
- Procesar los mensajes de una cola de mensajes en el segundo plano de una aplicación web mientras se comparten servicios comunes como
ILogger. - Una tarea en segundo plano iniciada con
Task.Run().
Básicamente, puede trasladar cualquiera de esas acciones a una tarea en segundo plano que implemente IHostedService.
La manera de añadir uno o varios IHostedServices dentro de tus WebHost o Host es registrándolos mediante el método de extensión AddHostedService en un WebHost de ASP.NET Core (o en un Host en .NET Core 2.1 y versiones posteriores). Básicamente, tiene que registrar los servicios hospedados en el inicio de la aplicación en Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
En ese código, el GracePeriodManagerService servicio hospedado es código real del microservicio empresarial de pedidos en eShopOnContainers, mientras que los otros dos son solo dos ejemplos adicionales.
La IHostedService ejecución de la tarea en segundo plano se coordina con el ciclo de vida de la aplicación, tanto en el host como en el microservicio. Registra tareas cuando se inicia la aplicación y tiene la oportunidad de realizar alguna acción correcta o limpiar cuando la aplicación se apaga.
Sin usar IHostedService, siempre podría iniciar un subproceso en segundo plano para ejecutar cualquier tarea. La diferencia está precisamente en el momento de cierre de la aplicación, cuando ese subproceso simplemente terminaría sin tener ocasión de ejecutar las acciones de limpieza correcta.
Interfaz IHostedService
Al registrar un IHostedService, .NET llama a los métodos StartAsync() y StopAsync() de su tipo IHostedService durante el inicio y la detención de la aplicación, respectivamente. Para obtener más información, consulte Interfaz IHostedService.
Como puede imaginar, puede crear varias implementaciones de IHostedService y registrar cada una de ellas en Program.cs, como se mostró anteriormente. Todos esos servicios hospedados se iniciarán y detendrán junto con el microservicio o la aplicación.
Como desarrollador, usted es responsable de manejar la acción de detención de sus servicios cuando el método StopAsync() es desencadenado por el host.
Implementación de IHostedService con una clase de servicio hospedada personalizada derivada de la clase base BackgroundService
Puede seguir adelante y crear su clase personalizada de servicio alojado desde cero e implementar IHostedService, tal como debe hacer al usar .NET Core 2.0 y versiones posteriores.
Sin embargo, dado que la mayoría de las tareas en segundo plano tendrán necesidades similares con respecto a la administración de tokens de cancelación y otras operaciones típicas, hay una clase base abstracta conveniente a la que se puede derivar, denominada BackgroundService (disponible desde .NET Core 2.1).
Esta clase proporciona el trabajo principal necesario para configurar la tarea en segundo plano.
El código siguiente es la clase base abstracta BackgroundService tal como se implementa en .NET.
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
Al derivar de la clase base abstracta anterior, gracias a esa implementación heredada, solo tiene que implementar el ExecuteAsync() método en su propia clase de servicio hospedada personalizada, como en el siguiente código simplificado de eShopOnContainers que sondea una base de datos y publica eventos de integración en event Bus cuando sea necesario.
public class GracePeriodManagerService : BackgroundService
{
private readonly ILogger<GracePeriodManagerService> _logger;
private readonly OrderingBackgroundSettings _settings;
private readonly IEventBus _eventBus;
public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
{
// Constructor's parameters validations...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"GracePeriodManagerService is starting.");
stoppingToken.Register(() =>
_logger.LogDebug($" GracePeriod background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"GracePeriod task doing background work.");
// This eShopOnContainers method is querying a database table
// and publishing events into the Event Bus (RabbitMQ / ServiceBus)
CheckConfirmedGracePeriodOrders();
try {
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
catch (TaskCanceledException exception) {
_logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
}
}
_logger.LogDebug($"GracePeriod background task is stopping.");
}
.../...
}
En este caso específico para eShopOnContainers, ejecuta un método de aplicación que consulta una tabla de base de datos que busca pedidos con un estado específico y, al aplicar cambios, está publicando eventos de integración a través del bus de eventos (debajo puede usar RabbitMQ o Azure Service Bus).
Por supuesto, podría ejecutar cualquier otra tarea en segundo plano empresarial, en su lugar.
De forma predeterminada, el token de cancelación se establece con un tiempo de espera de 5 segundos, aunque puede cambiar ese valor cuando construye WebHost utilizando la extensión UseShutdownTimeout de IWebHostBuilder. Esto significa que se espera que nuestro servicio se cancele en un plazo de 5 segundos; de lo contrario, se eliminará más abruptamente.
El código siguiente cambiaría ese tiempo a 10 segundos.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Diagrama resumen de clases
En la imagen siguiente se muestra un resumen visual de las clases e interfaces implicadas al implementar IHostedServices.
Figura 6-27. Diagrama de clases que muestra las varias clases e interfaces relacionadas con IHostedService
Diagrama de clases: IWebHost e IHost pueden hospedar muchos servicios, que heredan de BackgroundService, que implementa IHostedService.
Consideraciones y conclusiones de la implementación
Es importante tener en cuenta que la forma en que implemente el ASP.NET Core WebHost o .NET Host podría afectar a la solución final. Por ejemplo, si implementa su WebHost en IIS o en un servicio de Azure App Service normal, el host se puede cerrar debido a reciclajes del grupo de aplicaciones. Pero si va a desplegar su servidor como un contenedor en un orquestador como Kubernetes, puede controlar el número asegurado de instancias activas de su servidor. Además, podría considerar otros enfoques en la nube especialmente realizados para estos escenarios, como Azure Functions. Por último, si necesita que el servicio se ejecute todo el tiempo y esté implementando en un servidor de Windows Server, podría usar un servicio de Windows.
Pero incluso para un elemento WebHost implementado en un grupo de aplicaciones, hay escenarios, como el relleno o el vaciado de la memoria caché de la aplicación, en los que sería también aplicable.
La IHostedService interfaz proporciona una manera cómoda de iniciar tareas en segundo plano en una aplicación web de ASP.NET Core (en .NET Core 2.0 y versiones posteriores) o en cualquier proceso o host (a partir de .NET Core 2.1 con IHost). La principal ventaja es la oportunidad de obtener con la cancelación correcta un código de limpieza de sus tareas en segundo plano cuando se está cerrando el propio host.
Recursos adicionales
Creación de una tarea programada en ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlImplementación de IHostedService en ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceEjemplo de GenericHost con ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample