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. Gli sviluppatori Windows che usavano le prime versioni di .NET Framework potevano creare servizi Windows per questi motivi. 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. In questa sezione sono disponibili definizioni per alcuni di questi termini per renderne più evidente la finalità.

  • Servizio in background: fa riferimento al tipo BackgroundService.
  • Servizio ospitato: implementazioni di IHostedService o riferimento a IHostedService stesso.
  • Servizio a esecuzione prolungata: qualsiasi servizio eseguito in modo continuo.
  • Servizio Windows: l'infrastruttura del servizio Windows, in origine incentrata su .NET Framework, ma ora accessibile tramite .NET.
  • Servizio ruolo di lavoro: fa riferimento al modello Servizio ruolo di lavoro.

Modello di servizio di ruolo di lavoro

Il modello Servizio ruolo di lavoro è disponibile per l'interfaccia della riga di comando di .NET e 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:

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 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0 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 alias base.
  • Modifica della directory di lavoro in /app.
  • Impostazione dell'alias build dall'immagine mcr.microsoft.com/dotnet/sdk:8.0.
  • Modifica della directory di lavoro in /src.
  • Copia del contenuto e pubblicazione dell'app .NET:
  • Inoltro dell'immagine .NET SDK da mcr.microsoft.com/dotnet/runtime:8.0 (alias base).
  • 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="8.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
  </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:

Vedi anche