Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Sugestão
Este conteúdo é um trecho do eBook, .NET Microservices Architecture for Containerized .NET Applications, disponível no do .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
Tarefas em segundo plano e trabalhos agendados são algo que você pode precisar usar em qualquer aplicativo, independentemente de seguir ou não o padrão de arquitetura de microsserviços. A diferença ao usar uma arquitetura de microsserviços é que você pode implementar a tarefa em segundo plano em um processo/contêiner separado para hospedagem, para que possa reduzi-la/aumentar com base em sua necessidade.
De um ponto de vista genérico, no .NET chamamos esse tipo de tarefas de Serviços Hospedados, porque são serviços/lógica que você hospeda dentro do seu host/aplicativo/microsserviço. Observe que, nesse caso, o serviço hospedado significa simplesmente uma classe com a lógica de tarefa em segundo plano.
Desde o .NET Core 2.0, a estrutura fornece uma nova interface chamada IHostedService ajudando você a implementar facilmente serviços hospedados. A ideia básica é que pode-se registar várias tarefas em segundo plano (serviços hospedados) que são executadas em segundo plano enquanto o seu servidor ou anfitrião está em execução, como mostrado na imagem 6-26.
Figura 6-26. Usando IHostedService em um WebHost vs. um Host
ASP.NET Core 1.x e 2.x suporte IWebHost a processos em segundo plano em aplicações web. O .NET Core 2.1 e versões posteriores oferecem suporte IHost para processos em segundo plano com aplicativos de console simples. Observe a diferença feita entre WebHost e Host.
A WebHost (classe base implementando IWebHost) no ASP.NET Core 2.0 é o artefato de infraestrutura que utiliza para fornecer funcionalidades de servidor HTTP ao seu processo, tal como ao implementar uma aplicação web MVC ou um serviço de API web. Ele fornece todos os benefícios da nova infraestrutura no ASP.NET Core, permitindo-lhe usar injeção de dependência, inserir middlewares no pipeline de pedidos e similares. O WebHost usa estes mesmos IHostedServices para tarefas em segundo plano.
A Host (classe base ao implementar IHost) foi introduzida no .NET Core 2.1. Basicamente, a Host permite que você tenha uma infraestrutura semelhante à que você tem com WebHost (injeção de dependência, serviços hospedados, etc.), mas neste caso, você só quer ter um processo simples e mais leve como o host, sem nada relacionado a MVC, API Web ou recursos de servidor HTTP.
Portanto, pode escolher criar um processo de host especializado para gerir apenas os serviços hospedados com IHost, como um microsserviço feito apenas para hospedar o IHostedServices, ou alternativamente pode estender uma aplicação ASP.NET Core WebHost existente, como uma aplicação ASP.NET Core Web API ou MVC existente.
Cada abordagem tem prós e contras, dependendo do seu negócio e das necessidades de escalabilidade. A linha inferior é basicamente que, se suas tarefas em segundo plano não têm nada a ver com HTTP (IWebHost), você deve usar IHost.
Registrando serviços hospedados em seu WebHost ou Host
Vamos aprofundar mais sobre a interface IHostedService já que seu uso é bastante semelhante em um WebHost ou em um Host.
O SignalR é um exemplo de artefato usando serviços hospedados, mas você também pode usá-lo para coisas muito mais simples, como:
- Uma tarefa em segundo plano sondando um banco de dados à procura de alterações.
- Uma tarefa agendada que atualiza uma cache periodicamente.
- Uma implementação de QueueBackgroundWorkItem que permite que uma tarefa seja executada em um thread em segundo plano.
- Processar mensagens de uma fila de mensagens em segundo plano de uma aplicação web ao partilhar serviços comuns, como
ILogger. - Uma tarefa em segundo plano começou com
Task.Run().
Você pode basicamente descarregar qualquer uma dessas ações para uma tarefa em segundo plano que implementa IHostedService.
A forma de adicionar um ou vários IHostedServices ao seu WebHost ou Host é registrando-os através do método de extensão AddHostedService em um ASP.NET Core WebHost (ou em um Host no .NET Core 2.1 e posteriores). Basicamente, você tem que registrar os serviços hospedados dentro da inicialização do aplicativo em Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
Nesse código, o GracePeriodManagerService serviço hospedado é o código real do microsserviço de negócios Ordering no eShopOnContainers, enquanto os outros dois são apenas exemplos adicionais.
A IHostedService execução da tarefa em segundo plano é coordenada com o tempo de vida do aplicativo (host ou microsserviço). Você registra tarefas quando o aplicativo é iniciado e tem a oportunidade de fazer alguma ação normal ou limpeza quando o aplicativo está sendo desligado.
Sem usar IHostedService, pode sempre iniciar uma thread em segundo plano para executar qualquer tarefa. A diferença está justamente no momento de desligamento do aplicativo, quando esse thread simplesmente seria morto sem ter a oportunidade de executar ações de limpeza graciosas.
A interface IHostedService
Quando regista um IHostedService, o .NET chama os métodos StartAsync() e StopAsync() do tipo IHostedService durante o início e a parada do aplicativo, respetivamente. Para obter mais detalhes, consulte Interface IHostedService.
Como você pode imaginar, você pode criar várias implementações de IHostedService e registrar cada uma delas em Program.cs, como mostrado anteriormente. Todos esses serviços hospedados serão iniciados e interrompidos junto com o aplicativo/microsserviço.
Como desenvolvedor, você é responsável por lidar com a ação de parada de seus serviços quando StopAsync() o método é acionado pelo host.
Implementando IHostedService com uma classe de serviço hospedada personalizada derivada da classe base BackgroundService
Você pode prosseguir e criar a sua própria classe personalizada de serviço hospedado do zero e implementar o IHostedService, conforme necessário ao utilizar o .NET Core 2.0 e posterior.
No entanto, como a maioria das tarefas em segundo plano terá necessidades semelhantes em relação ao gerenciamento de tokens de cancelamento e outras operações típicas, há uma classe base abstrata conveniente da qual você pode derivar, nomeada BackgroundService (disponível desde .NET Core 2.1).
Essa classe fornece o trabalho principal necessário para configurar a tarefa em segundo plano.
O próximo código é a classe base abstrata BackgroundService conforme implementado no .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();
}
}
Ao derivar da anterior classe base abstrata, graças à implementação herdada, só precisa implementar o método ExecuteAsync() na sua própria classe de serviço hospedada personalizada, como no código simplificado a seguir do eShopOnContainers, que consulta um banco de dados e publica eventos de integração no bus de eventos quando necessário.
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.");
}
.../...
}
Neste caso específico para eShopOnContainers, ele está executando um método de aplicativo que está consultando uma tabela de banco de dados procurando pedidos com um estado específico e, ao aplicar alterações, está publicando eventos de integração por meio do barramento de eventos (abaixo dele pode estar usando RabbitMQ ou Azure Service Bus).
Claro que, em vez disso, pode-se executar qualquer outra tarefa empresarial em segundo plano.
Por padrão, o token de cancelamento é definido com um tempo limite de 5 segundos, embora você possa alterar esse valor ao criar seu WebHost usando a extensão UseShutdownTimeout do IWebHostBuilder. Isto significa que o nosso serviço deverá ser cancelado dentro de 5 segundos, caso contrário será eliminado de forma mais abrupta.
O código a seguir estaria alterando esse tempo para 10 segundos.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Diagrama resumo de classes
A imagem a seguir mostra um resumo visual das classes e interfaces envolvidas na implementação do IHostedServices.
Figura 6-27. Diagrama de classes mostrando as várias classes e interfaces relacionadas a IHostedService
Diagrama de classes: IWebHost e IHost podem hospedar muitos serviços, que herdam de BackgroundService, que implementa IHostedService.
Considerações e conclusões sobre a implantação
É importante observar que a maneira como você implanta seu ASP.NET Core WebHost ou .NET Host pode afetar a solução final. Por exemplo, se você implantar o seu WebHost no IIS ou num Serviço de Aplicativo do Azure regular, o seu host poderá ser desligado devido à reciclagem dos pools de aplicações. Mas se estiver a implementar o seu anfitrião como um contentor num orquestrador como o Kubernetes, pode controlar o número assegurado de instâncias ativas do seu anfitrião. Além disso, você pode considerar outras abordagens na nuvem especialmente feitas para esses cenários, como o Azure Functions. Finalmente, se você precisar que o serviço esteja em execução o tempo todo e estiver implantando em um Windows Server, poderá usar um Serviço do Windows.
Mas mesmo para um WebHost implantado num pool de aplicações, há cenários como repreencher ou limpar o cache em memória da aplicação que ainda seriam aplicáveis.
A IHostedService interface fornece uma maneira conveniente de iniciar tarefas em segundo plano em um aplicativo Web ASP.NET Core (no .NET Core 2.0 e versões posteriores) ou em qualquer processo/host (começando no .NET Core 2.1 com IHost). O principal benefício é a oportunidade proporcionada pelo cancelamento suave para limpar o código das suas tarefas em segundo plano quando o próprio host está a desligar-se.
Recursos adicionais
Criando uma tarefa agendada no ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlImplementando IHostedService no ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceExemplo GenericHost usando o ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample