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.
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 comporta un'interfaccia utente, ma le interfacce utente possono essere compilate intorno a esse. Nei primi giorni con .NET Framework, gli sviluppatori Windows potrebbero creare servizi Windows per questi scopi. Ora con .NET è possibile usare l'BackgroundService, che è un'implementazione di IHostedServiceo implementare il proprio.
Con .NET non si è più limitati a Windows. È possibile sviluppare servizi multipiattaforma in background. I servizi ospitati sono abilitati per il logging, la configurazione e l'iniezione di dipendenze (DI). Fanno parte della suite di librerie delle estensioni, ovvero sono fondamentali per tutti i carichi di lavoro .NET che funzionano con l'host generico .
Importante
L'installazione di .NET SDK installa anche il Microsoft.NET.Sdk.Worker
e il template 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 viene nascosto fino a quando non viene installato il carico di lavoro facoltativo ASP.NET e sviluppo Web.
Terminologia
Molti termini vengono usati erroneamente come sinonimi. Questa sezione definisce alcuni di questi termini per renderli più evidenti in questo articolo.
- Servizio in background: il tipo di BackgroundService.
- servizio ospitato: implementazioni di IHostedServiceo del IHostedService stesso.
- servizio a esecuzione prolungata: Qualsiasi servizio eseguito in maniera continuativa.
- servizio Windows: l'infrastruttura del servizio Windows, in origine incentrata su .NET Framework, ma ora accessibile tramite .NET.
- Servizio lavoratore: Il modello Servizio lavoratore.
Modello di servizio di lavoro
Il modello del servizio di lavoro è disponibile nell'interfaccia della riga di comando di .NET e in Visual Studio. Per altre informazioni, vedere - interfaccia della riga di comando di .NET, dotnet 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();
Classe Program
precedente:
- Crea un HostApplicationBuilder.
- Chiama AddHostedService per registrare il
Worker
come servizio ospitato. - Compila un IHost dal generatore.
- Chiama
Run
sull'istanzahost
, che avvia l'applicazione.
Impostazioni predefinite del modello
Il modello Worker non abilita il Garbage Collection (GC) del server per impostazione predefinita, poiché esistono numerosi fattori che svolgono un ruolo nella determinazione della necessità. Gli scenari che richiedono servizi di lunga durata devono considerare le implicazioni sulle prestazioni di questa impostazione predefinita. Per abilitare il server GC, aggiungere il nodo ServerGarbageCollection
al file di progetto:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
compromessi e considerazioni
Abilitato | Disabile |
---|---|
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ò risparmiare 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 della raccolta dei rifiuti per processi effimeri o di breve durata. |
Per altre informazioni sulle considerazioni sulle prestazioni, vedere Server GC. Per ulteriori informazioni sulla configurazione del server GC, vedere esempi di configurazione del server GC.
Classe di lavoro
Per quanto riguarda il 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. Il BackgroundService è un abstract class
e richiede la sottoclasse per implementare BackgroundService.ExecuteAsync(CancellationToken). Nell'implementazione del modello, il ExecuteAsync
esegue un ciclo una volta al secondo, registrando la data e l'ora correnti fino a quando il processo non viene segnalato per annullare.
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 il Microsoft.NET.Sdk.Worker
SDK e ha un riferimento esplicito al pacchetto Microsoft.Extensions.Hosting.
Contenitori e adattabilità nel cloud
Con la maggior parte dei carichi di lavoro .NET moderni, i contenitori sono un'opzione praticabile. Quando si crea un servizio a esecuzione prolungata dal modello di ruolo di lavoro 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 in genere si trova 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 Dockerfile includono:
- Impostazione dell'immagine di base da
mcr.microsoft.com/dotnet/runtime:8.0
come aliasbase
. - Cambiamento della directory di lavoro a /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
). - Copiare l'output della compilazione pubblicato dalla cartella /publish.
- Definendo il punto di ingresso, che viene delegato a
dotnet App.BackgroundService.dll
.
Consiglio
McR in mcr.microsoft.com
è l'acronimo di "Microsoft Container Registry" ed è il catalogo dei contenitori diffuso di Microsoft dall'hub Docker ufficiale. L'articolo del catalogo dei contenitori sindacato da Microsoft contiene ulteriori dettagli.
Quando si usa Docker come strategia di distribuzione per il servizio 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.6" />
<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
. Quando il supporto Docker Microsoft.VisualStudio.Azure.Containers.Tools.Targets
viene selezionato dal modello, il pacchetto NuGet viene aggiunto automaticamente come riferimento al pacchetto.
Per altre informazioni su Docker con .NET, vedere Guida: trasformare un'app .NET in un container. Per altre informazioni sulla distribuzione in Azure, vedere Esercitazione: Distribuire un servizio di lavoro in Azure.
Importante
Se si vuole sfruttare 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 ciclo di vita e vengono chiamati durante gli eventi di avvio e arresto dell'host, rispettivamente.
Nota
Per garantire che il servizio inizi e/o si arresti correttamente, quando si esegue l'override dei metodi StartAsync o StopAsync, è necessario chiamare e await
il metodo di classe base
.
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 implementare completamente il proprio.
Completamento del segnale
Negli scenari più comuni non è necessario segnalare in modo esplicito il completamento di un servizio ospitato. Quando l'host avvia i servizi, questi sono progettati per funzionare 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 BackgroundService.ExecuteAsync(CancellationToken) non esegue il ciclo e al termine chiama IHostApplicationLifetime.StopApplication().
Importante
Questo segnalerà all'host che deve arrestarsi e, senza questa chiamata a StopApplication
, l'host continuerà a funzionare indefinitamente. Se si prevede di eseguire un servizio ospitato di breve durata (scenario eseguito una sola volta) e si vuole usare il modello di lavoro, è necessario chiamare StopApplication
per segnalare l'arresto dell'host.
Per altre informazioni, vedere:
- host generico .NET: IHostApplicationLifetime
- host generico .NET: spegnimento dell'host
- Host generico .NET: processo di arresto dell'host
Approccio alternativo
Per un'app di breve durata che richiede inserimento, registrazione e configurazione delle dipendenze, usare l'host generico .NET anziché il modello Worker. In questo modo è possibile usare queste funzionalità senza la Worker
classe . Un semplice esempio di un'app di breve durata che usa l'host generico potrebbe definire un file di progetto simile al seguente:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ShortLived.App</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
</ItemGroup>
</Project>
È possibile che la Program
classe sia simile alla seguente:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<JobRunner>();
using var host = builder.Build();
try
{
var runner = host.Services.GetRequiredService<JobRunner>();
await runner.RunAsync();
return 0; // success
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Unhandled exception occurred during job execution.");
return 1; // failure
}
Il codice precedente crea un JobRunner
servizio, ovvero una classe personalizzata che contiene la logica per l'esecuzione del processo. Il RunAsync
metodo viene chiamato su JobRunner
e, se viene completato correttamente, l'app restituisce 0
. Se si verifica un'eccezione non gestita, registra l'errore e restituisce 1
.
In questo scenario semplice, la JobRunner
classe potrebbe essere simile alla seguente:
using Microsoft.Extensions.Logging;
internal sealed class JobRunner(ILogger<JobRunner> logger)
{
public async Task RunAsync()
{
logger.LogInformation("Starting job...");
// Simulate work
await Task.Delay(1000);
// Simulate failure
// throw new InvalidOperationException("Something went wrong!");
logger.LogInformation("Job completed successfully.");
}
}
Ovviamente è necessario aggiungere logica reale al RunAsync
metodo , ma questo esempio dimostra come usare l'host generico per un'app di breve durata senza la necessità di una Worker
classe e senza la necessità di segnalare in modo esplicito il completamento dell'host.
Vedere anche
- BackgroundService esercitazioni sulle sottoclassi della categoria D0:
- Implementazione di IHostedService personalizzata: