Workerdienste in .NET

Es gibt zahlreiche Gründe für die Erstellung von Diensten mit langer Laufzeit, z. B.:

  • Verarbeiten CPU-intensiver Daten.
  • Queuing von Arbeitselementen im Hintergrund.
  • Ausführen eines zeitbasierten Vorgangs nach einem Zeitplan.

Die Verarbeitung von Hintergrunddiensten umfasst in der Regel keine Benutzeroberfläche, aber Benutzeroberflächen können um sie herum erstellt werden. Aus diesen Gründen konnten Windows-Entwickler in den Anfängen von .NET Framework Windows-Dienste erstellen. Nun können Sie mit .NET den BackgroundService, eine Implementierung von IHostedService, verwenden oder ihren eigenen implementieren.

Mit .NET sind Sie nicht mehr auf Windows beschränkt. Sie können plattformübergreifende Hintergrunddienste entwickeln. Gehostete Dienste sind protokollierungs-, konfigurations- und Dependency Injection(DI)-bereit. Sie sind Teil der Erweiterungssammlung von Bibliotheken, was bedeutet, dass sie für alle .NET-Workloads, die mit dem generischen Host arbeiten, von grundlegender Bedeutung sind.

Wichtig

Bei der Installation des .NET SDK werden auch Microsoft.NET.Sdk.Worker und die Workervorlage installiert. Anders ausgedrückt: Nach der Installation des .NET SDK können Sie mithilfe des Befehls dotnet new worker einen neuen Worker erstellen. Wenn Sie Visual Studio verwenden, wird die Vorlage ausgeblendet, bis die optionale ASP.NET- und Webentwicklungsworkload installiert ist.

Begriff

Viele Begriffe werden fälschlicherweise synonym verwendet. In diesem Abschnitt werden einige dieser Begriffe definiert, um ihre Absicht in diesem Artikel deutlicher zu machen.

  • Hintergrunddienst: Der BackgroundService-Typ
  • Gehosteter Dienst: Implementierungen von IHostedService, oder der IHostedService selbst
  • Dienst mit langer Ausführungszeit: Jeder Dienst, der kontinuierlich ausgeführt wird.
  • Windows-Dienst: Die Infrastruktur des Windows-Diensts, die ursprünglich .NET Framework-zentrisch war, aber jetzt über .NET zugänglich ist
  • Workerdienst: Die Workerdienstvorlage

Workerdienstvorlage

Die Workerdienstvorlage ist für die .NET CLI und Visual Studio verfügbar. Weitere Informationen finden Sie unter .NET CLI, dotnet new worker: Vorlage. Die Vorlage besteht aus einer Program- und einer Worker -Klasse.

using App.WorkerService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

IHost host = builder.Build();
host.Run();

Die vorangehende Program-Klasse:

  • Erstellt eine HostApplicationBuilder.
  • Ruft AddHostedService auf, um Worker als gehosteten Dienst zu registrieren.
  • Erstellt einen IHost über den Generator.
  • Ruft Run für die host-Instanz auf, die die App ausführt.

Vorlagenstandards

Die Worker-Vorlage aktiviert standardmäßig keine Server Garbage Collection (GC), da es zahlreiche Faktoren gibt, die bei der Bestimmung ihrer Notwendigkeit eine Rolle spielen. In allen Szenarien, in denen Dienste mit langer Ausführungszeit erforderlich sind, sollten die Auswirkungen dieser Standardeinstellung auf die Leistung berücksichtigt werden. Um die Server-GC zu aktivieren, fügen Sie der Projektdatei den Knoten ServerGarbageCollection hinzu:

<PropertyGroup>
    <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>

Nachteile und Überlegungen

Aktiviert Disabled
Effiziente Speicherverwaltung: Übernimmt automatisch nicht genutzten Speicher, um Speicherverluste zu verhindern und die Ressourcennutzung zu optimieren. Verbesserte Echtzeitleistung: Verhindert potenzielle Pausen oder Unterbrechungen, die durch die Garbage Collection in latenzempfindlichen Anwendungen verursacht werden.
Langfristige Stabilität: Hilft Standard stabile Leistung in langfristigen Diensten zu gewährleisten, indem der Speicher über längere Zeiträume verwaltet wird. Ressourceneffizienz: Kann CPU- und Arbeitsspeicherressourcen in ressourcengeschränkten Umgebungen sparen.
Reduzierte Wartung: Minimiert den Bedarf an manueller Speicherverwaltung und vereinfacht die Wartung. Manuelle Speichersteuerung: Bietet eine differenzierte Kontrolle über den Speicher für spezialisierte Anwendungen.
Vorhersehbares Verhalten: Trägt zu konsistentem und vorhersagbarem Anwendungsverhalten bei. Geeignet für kurzlebige Prozesse: Minimiert den Aufwand der Garbage Collection für kurzlebige oder kurzlebige Prozesse.

Weitere Informationen zu Leistungsüberlegungen finden Sie unter Server-GC. Weitere Informationen zum Konfigurieren der Server-GC finden Sie unter Beispiele für die Konfiguration der Server-GC.

Worker-Klasse

Für den Worker bietet die Vorlage eine einfache Implementierung.

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);
        }
    }
}

Die vorangehende Worker-Klasse ist eine Unterklasse von BackgroundService, die IHostedService implementiert. Der BackgroundService ist ein abstract class und erfordert die Unterklasse, um BackgroundService.ExecuteAsync(CancellationToken) zu implementieren. In der Vorlagenimplementierung wird ein Mal pro Sekunde eine ExecuteAsync-Schleife erstellt, und das aktuelle Datum und die aktuelle Uhrzeit werden protokolliert, bis der Prozess abgebrochen wird.

Die Projektdatei.

Die Workervorlage basiert auf der folgenden Sdk-Projektdatei:

<Project Sdk="Microsoft.NET.Sdk.Worker">

Weitere Informationen finden Sie unter .NET-Projekt-SDKs.

NuGet-Paket

Eine App, die auf der Workervorlage basiert, verwendet das Microsoft.NET.Sdk.Worker SDK und verfügt über einen expliziten Paketverweis auf das Microsoft.Extensions.Hosting-Paket.

Container und Cloudanpassungsfähigkeit

Bei den meisten modernen .NET-Workloads sind Container eine praktikable Option. Wenn Sie über die Workervorlage in Visual Studio einen Dienst mit langer Ausführungszeit erstellen, können Sie Docker-Unterstützung verwenden. Dadurch wird eine Dockerfile-Datei erstellt, die Ihre .NET-App containerisiert. Eine Dockerfile-Datei ist eine Reihe von Anweisungen zum Erstellen eines Images. Bei .NET-Apps befindet sich die Dockerfile-Datei in der Regel im Stamm des Verzeichnisses neben einer Projektmappendatei.

# 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"]

Die obigen Dockerfile-Schritte umfassen Folgendes:

  • Festlegen des Basisimages von mcr.microsoft.com/dotnet/runtime:8.0 als Alias base.
  • Ändern des Arbeitsverzeichnisses in /app.
  • Festlegen des build-Alias aus dem Image mcr.microsoft.com/dotnet/sdk:8.0.
  • Ändern des Arbeitsverzeichnisses in /src.
  • Kopieren des Inhalts und Veröffentlichen der .NET-App:
  • Neuschichten des .NET SDK-Image aus mcr.microsoft.com/dotnet/runtime:8.0 (base-Alias).
  • Kopieren der veröffentlichten Buildausgabe aus /publish.
  • Definieren des Einstiegspunkts, der an dotnet App.BackgroundService.dll delegiert wird.

Tipp

MCR in mcr.microsoft.com steht für „Microsoft Container Registry“ und ist der syndizierte Containerkatalog von Microsoft aus dem offiziellen Docker-Hub. Der Artikel Microsoft syndicates container catalog enthält zusätzliche Details.

Wenn Sie Docker als Bereitstellungsstrategie für Ihren .NET-Workerdienst verwenden, sind in der Projektdatei einige Aspekte zu berücksichtigen:

<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>

In der obigen Projektdatei gibt das <DockerDefaultTargetOS>-Element Linux als Ziel an. Sollen Windows-Container das Ziel sein, verwenden Sie stattdessen Windows. Das Microsoft.VisualStudio.Azure.Containers.Tools.Targets NuGet-Paket wird automatisch als Paketverweis hinzugefügt, wenn Docker-Unterstützung aus der Vorlage ausgewählt wird.

Weitere Informationen zu Docker mit .NET finden Sie unter Tutorial: Containerisieren einer .NET-App. Weitere Informationen zur Bereitstellung in Azure finden Sie unter Tutorial: Bereitstellen eines Workerdiensts in Azure.

Wichtig

Wenn Sie Benutzergeheimnisse mit der Workervorlage nutzen möchten, müssen Sie explizit auf das NuGet-Paket Microsoft.Extensions.Configuration.UserSecrets verweisen.

Erweiterbarkeit gehosteter Dienste

In der IHostedService-Schnittstelle sind zwei Methoden definiert:

Diese beiden Methoden dienen als Lebenszyklusmethoden – sie werden jeweils beim Starten und Beenden des Hosts aufgerufen.

Hinweis

Wenn die StartAsync- oder StopAsync-Methoden außer Kraft gesetzt werden, müssen Sie die base-Klassenmethode aufrufen und await verwenden, um sicherzustellen, dass der Dienst ordnungsgemäß gestartet und/oder beendet wird.

Wichtig

Die Schnittstelle dient als generische Parametereinschränkung für die Erweiterungsmethode AddHostedService<THostedService>(IServiceCollection), was bedeutet, dass nur Implementierungen zulässig sind. Sie können den bereitgestellten BackgroundService mit einer Unterklasse verwenden oder gänzlich ihren eigenen implementieren.

Signalisieren des Abschlusses

In den meisten gängigen Szenarien müssen Sie den Abschluss eines gehosteten Diensts nicht explizit signalisieren. Wenn der Host die Dienste startet, werden sie per Design so lange ausgeführt, bis der Host beendet wird. In einigen Szenarien müssen Sie jedoch möglicherweise den Abschluss der gesamten Hostanwendung signalisieren, wenn der Dienst abgeschlossen ist. Um den Abschluss zu signalisieren, ziehen Sie die folgende Worker-Klasse in Betracht:

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();
    }
}

Im vorherigen Code wird die ExecuteAsync-Methode nicht in einer Schleife ausgeführt. Wenn sie abgeschlossen ist, ruft sie IHostApplicationLifetime.StopApplication() auf.

Wichtig

Dadurch wird dem Host signalisiert, dass er beendet werden soll, und ohne den Aufruf StopApplication wird der Host auf unbestimmte Zeit weiter ausgeführt.

Weitere Informationen finden Sie unter

Siehe auch