Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Dica
Esse conteúdo é um trecho do eBook, arquitetura de microsserviços do .NET para aplicativos .NET em contêineres, disponível em 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 talvez seja necessário usar em qualquer aplicativo, independentemente de ele 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 você possa reduzi-la/aumentar de acordo com sua necessidade.
Do ponto de vista genérico, no .NET, chamamos esses tipos de tarefas de Serviços Hospedados, pois são serviços/lógica hospedados em seu host/aplicativo/microsserviço. Observe que, nesse caso, o serviço hospedado significa simplesmente uma classe com a lógica da tarefa em segundo plano.
Desde o .NET Core 2.0, a estrutura fornece uma nova interface chamada IHostedService ajudando você a implementar facilmente os serviços hospedados. A ideia básica é que você pode registrar várias tarefas em segundo plano (serviços hospedados) que são executadas em segundo plano enquanto seu host ou host da Web está em execução, conforme mostrado na imagem 6-26.
Figura 6-26. Usando IHostedService em WebHost versus um Host
ASP.NET Core 1.x e 2.x oferecem suporte IWebHost
para processos em segundo plano em aplicativos web. O .NET Core 2.1 e versões posteriores dão suporte IHost
a processos em segundo plano com aplicativos de console simples. Observe a diferença feita entre WebHost
e Host
.
Um WebHost
(classe base implementando IWebHost
) no ASP.NET Core 2.0 é o artefato de infraestrutura que você utiliza para oferecer funcionalidades de servidor HTTP ao seu processo, como ao implementar um aplicativo web MVC ou um serviço de API Web. Ele fornece todos os benefícios da nova infraestrutura no ASP.NET Core, permitindo que você use a injeção de dependência, insira middleware no pipeline da solicitação e similares. O WebHost
usa esses mesmos IHostedServices
para tarefas em segundo plano.
Uma Host
(classe base implementando IHost
) foi introduzida no .NET Core 2.1. Basicamente, um Host
permite que você tenha uma infraestrutura semelhante à que você tem com WebHost
(injeção de dependência, serviços hospedados etc.), mas nesse caso, você só deseja ter um processo simples e mais leve como o host, sem nada relacionado aos recursos de MVC, API Web ou servidor HTTP.
Portanto, você pode escolher e criar um processo de host especializado com IHost
para lidar com os serviços hospedados e nada mais, como um microsserviço feito apenas para hospedar o IHostedServices
, ou você pode, como alternativa, estender um ASP.NET Core WebHost
existente, como uma API Web do ASP.NET Core existente ou um aplicativo MVC.
Cada abordagem tem prós e contras, dependendo das suas necessidades de negócios e escalabilidade. A linha de fundo é basicamente que, se suas tarefas em segundo plano não tiverem nada a ver com HTTP (IWebHost
) você deve usar IHost
.
Registrando serviços hospedados em seu WebHost ou Host
Vamos analisar mais detalhadamente a interface IHostedService
, já que seu uso é bastante semelhante em um WebHost
ou em um Host
.
O SignalR é um exemplo de um 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 em busca de alterações.
- Uma tarefa agendada que atualiza o cache periodicamente.
- Uma implementação de QueueBackgroundWorkItem que permite que uma tarefa seja executada em um thread em segundo plano.
- Processamento de mensagens de uma fila de mensagens em segundo plano de um aplicativo Web enquanto se compartilham serviços comuns como
ILogger
. - Uma tarefa em segundo plano iniciada com
Task.Run()
.
Você pode basicamente delegar qualquer uma dessas ações para uma tarefa em segundo plano que implementa IHostedService
.
A maneira como você adiciona um ou vários IHostedServices
no WebHost
ou Host
é registrando-os por meio do AddHostedServicemétodo de extensão em um ASP.NET CoreWebHost
(ou em um Host
no .NET Core 2.1 e posterior). Basicamente, você precisa registrar os serviços hospedados na inicialização do aplicativo no Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
Nesse código, o serviço hospedado GracePeriodManagerService
é o código real do microsserviço de negócios de Ordenação em eShopOnContainers, enquanto os outros dois são apenas dois exemplos adicionais.
A IHostedService
execução da tarefa em segundo plano é coordenada com o tempo de vida do aplicativo (host ou microsserviço, nesse caso). Você registra tarefas quando o aplicativo é iniciado e tem a oportunidade de realizar alguma ação elegante ou limpeza quando o aplicativo está sendo desligado.
Sem usar IHostedService
, você sempre pode iniciar um thread em segundo plano para executar qualquer tarefa. A diferença é precisamente no momento do desligamento do aplicativo, quando aquela thread simplesmente seria encerrada sem ter a oportunidade de executar ações de limpeza adequadas.
A interface IHostedService
Quando você registra um IHostedService
, o .NET chama os métodos StartAsync()
e StopAsync()
do tipo IHostedService
durante o início e a parada do aplicativo, respectivamente. Para obter mais detalhes, consulte a interface IHostedService.
Como você pode imaginar, você pode criar várias implementações de IHostedService e registrar cada uma delas em Program.cs, conforme 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 interrupção de seus serviços quando StopAsync()
o método é disparado pelo host.
Implementando IHostedService com uma classe de serviço hospedada personalizada derivada da classe base BackgroundService
Vá em frente e crie sua classe de serviço hospedado personalizada do zero e implemente o IHostedService
, como você precisa fazer ao usar 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 o .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 backgroundService abstrata, 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 classe base abstrata anterior, graças a essa implementação herdada, você só precisa implementar o ExecuteAsync()
método em sua própria classe de serviço hospedado personalizada, como no código simplificado a seguir do eShopOnContainers, que está sondando um banco de dados e publicando eventos de integração no Barramento 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, que 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 as alterações, ele está publicando eventos de integração por meio de um barramento de evento (sob ele, pode estar usando RabbitMQ ou Barramento de Serviço do Azure).
Claro, você pode executar qualquer outra tarefa administrativa em segundo plano.
Por padrão, o token de cancelamento é configurado com um tempo limite de 5 segundos, embora você possa alterar esse valor ao configurar seu WebHost
com a extensão UseShutdownTimeout
do IWebHostBuilder
. Isso significa que nosso serviço deve ser cancelado dentro de 5 segundos, senão ele será terminado 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 ao implementar IHostedServices.
Figura 6-27. Diagrama de classe mostrando as várias classes e interfaces relacionadas ao IHostedService
Diagrama de classe: IWebHost e IHost podem hospedar muitos serviços, que herdam do BackgroundService, que implementa o IHostedService.
Considerações de implantação e conclusões
É 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 WebHost
no IIS ou em um Serviço de Aplicativo do Azure normal, o host poderá desligar devido a reciclagens do pool de aplicativos. Mas se você estiver implantando seu host como um contêiner em um orquestrador como o Kubernetes, poderá controlar o número garantido de instâncias ativas do seu host. Além disso, você pode considerar outras abordagens na nuvem especialmente feitas para esses cenários, como o Azure Functions. Por fim, 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.
Porém, mesmo para um WebHost
implantado em um pool de aplicativos, há cenários como repopulação ou liberação do cache em memória do aplicativo em que isso ainda seria aplicável.
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 que você obtém com o cancelamento normal para o código de limpeza de suas tarefas em segundo plano quando o host em si está sendo desligado.
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 o IHostedService no ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceExemplo de GenericHost usando ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample