Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Suggerimento
Questo contenuto è un estratto dell'eBook, Architettura di microservizi .NET per applicazioni .NET containerizzati, disponibile in documentazione .NET o come PDF scaricabile gratuitamente leggibile offline.
Le attività in background e i processi pianificati possono essere necessari in qualsiasi applicazione, indipendentemente dal fatto che se segue o meno il modello di architettura dei microservizi. La differenza quando si usa un'architettura di microservizi consiste nel fatto che è possibile implementare l'attività in background in un processo/contenitore separato per l'hosting, in modo da poterla ridimensionare verso il basso o verso l'alto in base alle esigenze.
Da un punto di vista generico, in .NET è stato chiamato questo tipo di attività Servizi ospitati, perché sono servizi/logica che si ospitano all'interno dell'host/applicazione/microservizio. Nota che in questo caso il servizio ospitato si riferisce semplicemente a una classe con la logica dell'attività in background.
A partire da .NET Core 2.0, il framework fornisce una nuova interfaccia denominata IHostedService che consente di implementare facilmente i servizi ospitati. L'idea di base è che è possibile registrare più attività in background (servizi ospitati) che vengono eseguite continuamente mentre l'host web o l'host è attivo, come illustrato nell'immagine 6-26.
Figura 6-26. Uso di IHostedService in un WebHost rispetto a un host
ASP.NET Core 1.x e 2.x supportano IWebHost per i processi in background nelle app Web. .NET Core 2.1 e versioni successive supportano IHost i processi in background con app console semplici. Si noti la differenza tra WebHost e Host.
Una WebHost (classe base che implementa IWebHost) in ASP.NET Core 2.0 è l'artefatto dell'infrastruttura usato per fornire funzionalità del server HTTP al processo, ad esempio quando si implementa un'app Web MVC o un servizio API Web. Offre tutte le nuove funzionalità dell'infrastruttura in ASP.NET Core, consentendo di usare l'iniezione di dipendenze, inserire i middleware nella pipeline di richiesta e altro ancora.
WebHost usa gli stessi IHostedServices per le attività in background.
Una Host (classe di base che implementa IHost) è stata introdotta in .NET Core 2.1. Fondamentalmente, un Host consente di avere un'infrastruttura simile a quella con WebHost (inserimento delle dipendenze, servizi ospitati e così via), ma in questo caso si vuole solo avere un processo semplice e più leggero come host, con nulla correlato alle funzionalità MVC, API Web o server HTTP.
Pertanto, è possibile scegliere e creare un processo host specializzato con IHost per gestire i servizi ospitati e nient'altro, ad esempio un microservizio creato solo per l'hosting di IHostedServicesoppure è possibile estendere in alternativa un ASP.NET Core WebHostesistente, ad esempio un'API Web principale di ASP.NET esistente o un'app MVC.
Ogni approccio presenta vantaggi e svantaggi a seconda delle esigenze aziendali e di scalabilità. La riga inferiore è fondamentalmente che se le attività in background non hanno nulla a che fare con HTTP (IWebHost) è consigliabile usare IHost.
Registrazione dei servizi ospitati nel tuo WebHost o host
Approfondiamo ulteriormente l'interfaccia IHostedService poiché il suo utilizzo è molto simile in un WebHost o in un Host.
SignalR è un esempio di artefatto che usa servizi ospitati, ma è anche possibile usarlo per operazioni molto più semplici, ad esempio:
- Attività in background che esegue il polling di un database alla ricerca di modifiche.
- Un'attività pianificata che aggiorna periodicamente alcune cache.
- Implementazione di QueueBackgroundWorkItem che consente l'esecuzione di un'attività in un thread in background.
- Elaborazione dei messaggi da una coda di messaggi nel background di un'app Web mentre si condividono servizi comuni quali
ILogger. - Un'attività in background è stata avviata con
Task.Run().
In pratica, è possibile delegare una qualsiasi di queste azioni a un'attività in background che implementa IHostedService.
Il modo in cui si aggiungono uno o più IHostedServices al proprio WebHost o Host consiste nel registrarli tramite il metodo di estensione AddHostedService in un ASP.NET Core WebHost (o in un Host in .NET Core 2.1 e versioni successive a .NET Core 2.1). In pratica, è necessario registrare i servizi ospitati all'avvio dell'applicazione in Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
In tale codice, il GracePeriodManagerService servizio ospitato è un codice reale del microservizio business Ordering in eShopOnContainers, mentre gli altri due sono solo due esempi aggiuntivi.
L'esecuzione IHostedService dell'attività in background è coordinata con la durata dell'applicazione (host o microservizio). Le attività vengono registrate all'avvio dell'applicazione e si ha la possibilità di eseguire un'azione delicata o una pulizia ordinata quando l'applicazione viene chiusa.
Senza usare IHostedService, è sempre possibile avviare un thread in background per eseguire qualsiasi attività. La differenza è al momento dell'arresto dell'app quando quel thread viene semplicemente interrotto senza avere la possibilità di eseguire azioni di pulizia appropriata.
Interfaccia IHostedService
Quando si registra un IHostedService, .NET richiama rispettivamente i metodi StartAsync() e StopAsync() del tuo tipo IHostedService durante l'avvio e l'arresto dell'applicazione. Per altri dettagli, vedere Interfaccia IHostedService.
Come si può immaginare, è possibile creare più implementazioni di IHostedService e registrarle in Program.cs, come illustrato in precedenza. Tutti i servizi ospitati verranno avviati e arrestati insieme all'applicazione o al microservizio.
Gli sviluppatori sono responsabili della gestione dell'azione di arresto dei servizi quando StopAsync() il metodo viene attivato dall'host.
Implementazione di IHostedService con una classe di servizio ospitata personalizzata che deriva dalla classe di base BackgroundService
È possibile procedere e creare la classe di servizio ospitata personalizzata da zero e implementare IHostedService, come è necessario fare quando si usa .NET Core 2.0 e versioni successive.
Tuttavia, poiché la maggior parte delle attività in background avrà esigenze simili per quanto riguarda la gestione dei token di annullamento e altre operazioni tipiche, esiste una comoda classe base astratta da cui è possibile derivare, denominato BackgroundService (disponibile a partire da .NET Core 2.1).
Tale classe fornisce il lavoro principale necessario per configurare l'attività in background.
Il codice successivo è la classe di base Abstract BackgroundService implementata in .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();
}
}
Quando si deriva dalla classe base astratta precedente, grazie a tale implementazione ereditata, è sufficiente implementare il metodo nella classe del servizio ospitato personalizzata, come nel codice semplificato seguente di eShopOnContainers che esegue il ExecuteAsync() polling di un database e pubblica gli eventi di integrazione nel bus di eventi quando necessario.
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.");
}
.../...
}
In questo caso specifico per eShopOnContainers, esegue un metodo dell'applicazione che esegue una query su una tabella di database che cerca ordini con uno stato specifico e, quando si applicano modifiche, pubblica eventi di integrazione tramite il bus di eventi (sotto di esso è possibile usare RabbitMQ o il bus di servizio di Azure).
È possibile eseguire, in alternativa, qualsiasi altra attività aziendale in background.
Per impostazione predefinita, il token di annullamento viene impostato con un timeout di 5 secondi, anche se è possibile modificare tale valore quando si compila WebHost usando l'estensione UseShutdownTimeout di IWebHostBuilder. Questo significa che ci si aspetta che il nostro servizio si interrompa entro 5 secondi, altrimenti verrà terminato più bruscamente.
Il codice seguente cambierebbe tale tempo su 10 secondi.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Diagramma di riepilogo delle classi
L'immagine seguente mostra un riepilogo visivo delle classi e delle interfacce coinvolte durante l'implementazione di IHostedServices.
Figura 6-27. Diagramma classi che mostra più classi e interfacce correlate a IHostedService
Diagramma classi: IWebHost e IHost possono ospitare molti servizi, che ereditano da BackgroundService, che implementa IHostedService.
Considerazioni sulla distribuzione e aspetti principali
È importante notare che il modo in cui si distribuisce il ASP.NET Core WebHost o .NET Host potrebbe influire sulla soluzione finale. Ad esempio, se si distribuisce WebHost su IIS o su un normale servizio applicativo di Azure, l'host può essere arrestato a causa dei ricicli del pool di applicazioni. Tuttavia, se si distribuisce l'host come contenitore in un agente di orchestrazione come Kubernetes, è possibile controllare il numero garantito di istanze live dell'host. È anche possibile prendere in considerazione altri approcci nel cloud appositamente creati per questi scenari, ad esempio Funzioni di Azure. Infine, se è necessario che il servizio sia in esecuzione tutto il tempo e che si distribuisca in un Windows Server, è possibile usare un servizio Windows.
Ma anche per un'applicazione WebHost distribuita in un pool di app, esistono scenari come il ripopolamento o lo scaricamento della cache in memoria dell'applicazione che sarebbe ancora applicabile.
L'interfaccia IHostedService offre un modo pratico per avviare le attività in background in un'applicazione Web ASP.NET Core (in .NET Core 2.0 e versioni successive) o in qualsiasi processo/host (a partire da .NET Core 2.1 con IHost). Il vantaggio principale è l'opportunità che si ha con l'annullamento elegante per ripulire il codice delle attività in background quando l'host stesso viene spento.
Risorse aggiuntive
Creazione di un'attività pianificata in ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlImplementazione di IHostedService in ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceEsempio genericHost con ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample