Servizi ruolo di lavoro in .NET
Esistono numerosi motivi per la creazione di servizi a esecuzione prolungata, ad esempio:
- Elaborazione di dati a elevato utilizzo di CPU.
- Accodamento degli elementi di lavoro in background.
- Esecuzione di un'operazione basata sul tempo in base a una pianificazione.
L'elaborazione del servizio in background in genere non coinvolge un'interfaccia utente, ma è possibile creare interfacce utente correlate. Nei primi tempi con .NET Framework, gli sviluppatori Windows potevano creare servizi Windows per questi scopi. Con .NET è ora possibile usare BackgroundService, che è un'implementazione di IHostedServiceo implementarne uno personalizzato.
Con .NET non si è più limitati a Windows. È possibile sviluppare servizi in background multipiattaforma. I servizi ospitati sono pronti per la registrazione, la configurazione e l'inserimento delle dipendenze. Fanno parte della suite di librerie di estensioni, ovvero sono fondamentali per tutti i carichi di lavoro .NET che interagiscono con l'host generico.
Importante
L'installazione di .NET SDK installa anche Microsoft.NET.Sdk.Worker
e il modello di ruolo di lavoro. In altre parole, dopo aver installato .NET SDK, è possibile creare un nuovo ruolo di lavoro usando il comando dotnet new worker. Se si usa Visual Studio, il modello è nascosto fino a quando non viene installato il carico di lavoro facoltativo di ASP.NET e sviluppo Web.
Terminologia
Molti termini vengono usati erroneamente come sinonimi. Questa sezione definisce alcuni di questi termini per renderli più chiari in questo articolo.
- Servizio in background: il tipo BackgroundService.
- Servizio ospitato: implementazioni di IHostedService o dello stesso IHostedService.
- Servizio a esecuzione prolungata: qualsiasi servizio eseguito in modo continuo.
- Servizio Windows: l’infrastruttura del servizio Windows, in origine incentrata su .NET Framework, ora accessibile tramite .NET.
- Worker service: il modello worker service.
Modello di servizio di ruolo di lavoro
Il modello worker service è disponibile nell'interfaccia della riga di comando di .NET e in Visual Studio. Per altre informazioni, vedere Interfaccia della riga di comando di .NETdotnet new worker
- Modello. Il modello è costituito da una classe Program
e Worker
.
using App.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
La classe Program
precedente:
- Crea un oggetto HostApplicationBuilder.
- Chiama AddHostedService per registrare
Worker
come servizio ospitato. - Compila un IHost dal generatore.
- Chiama
Run
nell'istanza dihost
, che esegue l'app.
Impostazioni predefinite del modello
Il modello Worker non abilita la Garbage Collection (GC) del server per impostazione predefinita, poiché esistono numerosi fattori che svolgono un ruolo nella determinazione della necessità. Tutti gli scenari che richiedono servizi a esecuzione prolungata devono esaminare le implicazioni sulle prestazioni di questo valore predefinito. Per abilitare la Garbage Collection (GC) del server, aggiungere il nodo ServerGarbageCollection
al file di progetto:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
Compromessi e considerazioni
Attivata | Disabilitata |
---|---|
Gestione efficiente della memoria: recupera automaticamente la memoria inutilizzata per evitare perdite di memoria e ottimizzare l'utilizzo delle risorse. | Miglioramento delle prestazioni in tempo reale: evita potenziali pause o interruzioni causate da Garbage Collection nelle applicazioni sensibili alla latenza. |
Stabilità a lungo termine: consente di mantenere prestazioni stabili nei servizi a esecuzione prolungata gestendo la memoria in periodi prolungati. | Efficienza delle risorse: può preservare risorse di CPU e memoria in ambienti con vincoli di risorse. |
Manutenzione ridotta: riduce al minimo la necessità di gestione manuale della memoria, semplificando la manutenzione. | Controllo della memoria manuale: fornisce un controllo granulare sulla memoria per applicazioni specializzate. |
Comportamento prevedibile: contribuisce al comportamento coerente e prevedibile dell'applicazione. | Adatto per processi di breve durata: riduce al minimo il sovraccarico di Garbage Collection per processi temporanei o di breve durata. |
Per altre informazioni sulle considerazioni relative alle prestazioni, vedere Garbage Collection (GC) del server. Per altre informazioni sulla configurazione di Garbage Collection (GC) del server, vedere Esempi di configurazione di Garbage Collection (GC) del server.
Classe Worker
Per quanto riguarda Worker
, il modello fornisce un'implementazione semplice.
namespace App.WorkerService;
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}
La classe Worker
precedente è una sottoclasse di BackgroundService, che implementa IHostedService. BackgroundService è una abstract class
e richiede che la sottoclasse implementi BackgroundService.ExecuteAsync(CancellationToken). Nell'implementazione del modello, ExecuteAsync
esegue un ciclo una volta al secondo, registrando la data e l'ora correnti fino a quando il processo non riceve il segnale per l'annullamento.
File di progetto
Il modello Worker si basa sul file di progetto seguente Sdk
:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Per altre informazioni, vedere SDK di progetto .NET.
Pacchetto NuGet
Un'app basata sul modello Worker usa Microsoft.NET.Sdk.Worker
SDK e ha un riferimento esplicito al pacchetto Microsoft.Extensions.Hosting.
Contenitori e adattabilità cloud
I contenitori sono un'opzione praticabile con la maggior parte dei carichi di lavoro .NET moderni. Quando si crea un servizio a esecuzione prolungata dal modello Worker in Visual Studio, è possibile acconsentire esplicitamente al supporto di Docker. In questo modo viene creato un Dockerfile che crea un contenitore per l'app .NET. Un Dockerfile è un set di istruzioni per compilare un'immagine. Per le app .NET, il Dockerfile si trova in genere nella radice della directory accanto a un file di soluzione.
# See https://aka.ms/containerfastmode to understand how Visual Studio uses this
# Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:8.0@sha256:e6b552fd7a0302e4db30661b16537f7efcdc0b67790a47dbf67a5e798582d3a5 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /src
COPY ["background-service/App.WorkerService.csproj", "background-service/"]
RUN dotnet restore "background-service/App.WorkerService.csproj"
COPY . .
WORKDIR "/src/background-service"
RUN dotnet build "App.WorkerService.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "App.WorkerService.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App.WorkerService.dll"]
I passaggi precedenti per Dockerfile includono:
- Impostazione dell'immagine di base da
mcr.microsoft.com/dotnet/runtime:8.0
come aliasbase
. - Modifica della directory di lavoro in /app.
- Impostazione dell'alias
build
dall'immaginemcr.microsoft.com/dotnet/sdk:8.0
. - Modifica della directory di lavoro in /src.
- Copia del contenuto e pubblicazione dell'app .NET:
- L'app viene pubblicata usando il comando
dotnet publish
.
- L'app viene pubblicata usando il comando
- Inoltro dell'immagine .NET SDK da
mcr.microsoft.com/dotnet/runtime:8.0
(aliasbase
). - Copia dell'output di compilazione pubblicato da /publish.
- Definizione del punto di ingresso, che delega a
dotnet App.BackgroundService.dll
.
Suggerimento
MCR in mcr.microsoft.com
è l'acronimo di "Microsoft Container Registry" ed è il catalogo dei contenitori diffuso di Microsoft da Docker Hub ufficiale. L'articolo Catalogo dei contenitori diffuso di Microsoft contiene altri dettagli.
Quando si usa Docker come strategia di distribuzione per il servizio ruolo di lavoro .NET, nel file di progetto sono presenti alcune considerazioni:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WorkerService</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
Nel file di progetto precedente l'elemento <DockerDefaultTargetOS>
specifica Linux
come destinazione. Per impostare come destinazione i contenitori di Windows, usare invece Windows
. Il pacchetto NuGet Microsoft.VisualStudio.Azure.Containers.Tools.Targets
viene aggiunto automaticamente come riferimento al pacchetto quando il Supporto Docker viene selezionato dal modello.
Per altre informazioni su Docker con .NET, vedere Esercitazione: Distribuire un'app .NET in contenitori. Per altre informazioni sulla distribuzione in Azure, vedere Esercitazione: Distribuire un servizio ruolo di lavoro in Azure.
Importante
Se si vogliono sfruttare i Segreti utente con il modello Worker, è necessario fare riferimento in modo esplicito al pacchetto NuGet Microsoft.Extensions.Configuration.UserSecrets
.
Estendibilità del servizio ospitato
L'interfaccia IHostedService definisce due metodi:
Questi due metodi fungono da metodi del ciclo di vita e vengono chiamati rispettivamente durante gli eventi di avvio e arresto dell'host.
Nota
Quando si esegue l'override di metodi StartAsync o StopAsync, è necessario chiamare e usare await
per il metodo della classe base
per assicurarsi che il servizio venga avviato e/o arrestato correttamente.
Importante
L'interfaccia funge da vincolo di parametro di tipo generico nel metodo di estensione AddHostedService<THostedService>(IServiceCollection), ovvero sono consentite solo le implementazioni. È possibile usare il BackgroundService fornito con una sottoclasse o implementarne uno completamente personalizzato.
Segnalare il completamento
Negli scenari più comuni non è necessario segnalare in modo esplicito il completamento di un servizio ospitato. Quando l'host avvia i servizi, sono progettati per l'esecuzione fino a quando l'host non viene arrestato. In alcuni scenari, tuttavia, potrebbe essere necessario segnalare il completamento dell'intera applicazione host al termine del servizio. Per segnalare il completamento, considerare la classe Worker
seguente:
namespace App.SignalCompletionService;
public sealed class Worker(
IHostApplicationLifetime hostApplicationLifetime,
ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// TODO: implement single execution logic here.
logger.LogInformation(
"Worker running at: {Time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
// When completed, the entire app host will stop.
hostApplicationLifetime.StopApplication();
}
}
Nel codice precedente il metodo ExecuteAsync
non esegue il ciclo e al termine chiama IHostApplicationLifetime.StopApplication().
Importante
Questo segnalerà all'host che deve essere arrestato e senza questa chiamata a StopApplication
l'host continuerà a essere eseguito a tempo indeterminato.
Per altre informazioni, vedi:
- Host generico .NET: IHostApplicationLifetime
- Host generico .NET: arresto dell'host
- Host generico .NET: processo di arresto dell'hosting
Vedi anche
- Esercitazioni sulle sottoclassi BackgroundService:
- Implementazione personalizzata di IHostedService.