Megosztás:


Feldolgozói szolgáltatások a .NET-ben

A hosszú ideig futó szolgáltatások létrehozásának számos oka van, például:

  • Processzorigényes adatok feldolgozása.
  • Munkaelemek sorba állítása háttérben.
  • Időalapú művelet végrehajtása ütemezés szerint.

A háttérszolgáltatás-feldolgozás általában nem jár felhasználói felülettel (UI), de a felhasználói felületek ezek köré építhetők. A .NET-keretrendszer korai napjaiban a Windows-fejlesztők létrehozhatják a Windows-szolgáltatásokat ezekhez a célokhoz. A .NET-tel most már használhatja a BackgroundService, amely a IHostedServiceimplementációja, vagy implementálhatja a sajátját.

A .NET-ben már nem korlátozódik a Windowsra. Platformfüggetlen háttérszolgáltatásokat fejleszthet. Az üzemeltetett szolgáltatások naplózásra, konfigurálásra és függőséginjektálásra (DI) készek. Ezek a könyvtárbővítmény-csomag részei, ami azt jelenti, hogy elengedhetetlenek minden .NET terheléshez, amely a általános gazdagéppelműködik.

Fontos

A .NET SDK telepítése a Microsoft.NET.Sdk.Worker és a feldolgozósablont is telepíti. Más szóval a .NET SDK telepítése után létrehozhat egy új feldolgozót a dotnet új feldolgozó paranccsal. Ha Visual Studiót használ, a sablon elrejtve lesz, amíg az opcionális ASP.NET és a webfejlesztési számítási feladat nincs telepítve.

Terminológia

Sok kifejezést tévesen szinonimaként használnak. Ez a szakasz ezen kifejezések némelyikét határozza meg, hogy a jelen cikkben szereplő szándékuk egyértelműbb legyen.

  • Háttérszolgáltatás: A BackgroundService típusa.
  • Üzemeltetett szolgáltatás: A IHostedServiceimplementációi vagy maga a IHostedService.
  • hosszan futó szolgáltatás: Minden olyan szolgáltatás, amely folyamatosan fut.
  • Windows Service: A Windows Service-infrastruktúra, amely eredetileg .NET-keretrendszer-központú, de most már elérhető a .NET-en keresztül.
  • Munkás Szolgáltatás: A Munkás Szolgáltatás sablon.

Munka szolgáltatás sablon

A Worker Service-sablon a .NET CLI-ben és a Visual Studióban érhető el. További információ: .NET CLI, dotnet new worker – sablon. A sablon egy Program és Worker osztályból áll.

using App.WorkerService;

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

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

Az előző Program osztály:

  • Létrehoz egy HostApplicationBuilder.
  • Meghívja a AddHostedService-t, hogy regisztrálja a Worker-et üzemeltetett szolgáltatásként.
  • Létrehoz egy IHost-t az építőből.
  • Meghívja Run az alkalmazást futtató host-példányon.

A sablon alapértelmezései

A Feldolgozó sablon alapértelmezés szerint nem engedélyezi a kiszolgálói szemétgyűjtést (GC), mivel számos tényező játszik szerepet a szükségesség meghatározásában. A hosszú ideig futó szolgáltatásokat igénylő összes forgatókönyvnek figyelembe kell vennie ennek az alapértelmezettnek a teljesítménybeli következményeit. A szerver GC engedélyezéséhez adja hozzá a ServerGarbageCollection csomópontot a projektfájlhoz.

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

Kompromisszumok és szempontok

Engedélyezve Fogyatékos
Hatékony memóriakezelés: Automatikusan visszanyeri a nem használt memóriát a memóriavesztés elkerülése és az erőforrás-használat optimalizálása érdekében. Jobb valós idejű teljesítmény: Elkerüli a késésre érzékeny alkalmazásokban a szemétgyűjtés által okozott esetleges szüneteltetéseket vagy megszakításokat.
Hosszú távú stabilitás: Segít a hosszú ideig futó szolgáltatások stabil teljesítményének fenntartásában azáltal, hogy hosszabb ideig kezeli a memóriát. Erőforrás-hatékonyság: Csökkentheti a processzor- és memóriaerőforrásokat az erőforrás-korlátozott környezetekben.
Kevesebb karbantartás: Minimalizálja a manuális memóriakezelés szükségességét, egyszerűsítve a karbantartást. Manuális memóriavezérlés: Részletes vezérlést biztosít a speciális alkalmazások memóriája felett.
Kiszámítható viselkedés: Hozzájárul az alkalmazások konzisztens és kiszámítható viselkedéséhez. Rövid élettartamú folyamatokhoz alkalmas: Minimalizálja a szemétgyűjtés többletterhelését az ilyen folyamatok esetén.

A teljesítménnyel kapcsolatos további információkért lásd szerver GC. A kiszolgálói csoportházirend-objektumok konfigurálásával kapcsolatos további információkért lásd kiszolgálói csoportházirend-konfigurációs példákat.

Feldolgozói osztály

Ami a Worker-t illeti, a sablon egyszerű implementációt biztosít.

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

Az előző Worker osztály a BackgroundServicealosztálya , amely IHostedServiceimplementál. A BackgroundService egy abstract class, és megköveteli az alosztálytól a BackgroundService.ExecuteAsync(CancellationToken)implementálását. A sablon implementációjában a ExecuteAsync másodpercenként egyszer ismétlődik ciklusban, az aktuális dátumot és időt naplózva, amíg a folyamat jelzést nem kap a megszakításra.

A projektfájl

A Feldolgozó sablon a következő projektfájlra támaszkodik Sdk:

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

További információ: .NET-projekt SDK-k.

NuGet-csomag

A Feldolgozó sablonon alapuló alkalmazás a Microsoft.NET.Sdk.Worker SDK-t használja, és explicit csomaghivatkozással rendelkezik a Microsoft.Extensions.Hosting csomagra.

Tárolók és felhőadaptálás

A legtöbb modern .NET-számítási feladat esetén a tárolók életképes megoldást jelentenek. Ha hosszú ideig futó szolgáltatást hoz létre a Visual Studióban a Worker sablonból, akkor Docker-támogatás. Ezzel létrehoz egy Dockerfile, amely tárolóba helyezi a .NET-alkalmazást. A Dockerfile a rendszerképek létrehozásához szükséges utasítások készlete. A .NET-alkalmazások esetében a Dockerfile általában a megoldásfájl melletti könyvtár gyökerében található.

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

Az előző Dockerfile lépések a következők:

  • Az alapkép beállítása a mcr.microsoft.com/dotnet/runtime:8.0-ról basealias néven.
  • A munkakönyvtár megváltoztatása /app.
  • A build alias beállítása a mcr.microsoft.com/dotnet/sdk:8.0 képből.
  • A munkakönyvtár módosítása /src.
  • A tartalom másolása és a .NET-alkalmazás közzététele:
    • Az alkalmazás közzététele a dotnet publish paranccsal történik.
  • A .NET SDK kép újrétegezése a mcr.microsoft.com/dotnet/runtime:8.0-ról (a base alias).
  • A közzétett build kimenet másolása a /publish.
  • A belépési pont meghatározása, amely a dotnet App.BackgroundService.dll-ra delegál.

Tipp

A mcr.microsoft.com MCR a "Microsoft Container Registry" rövidítése, amely a Microsoft szindikált konténerkatalógusa a hivatalos Docker hubról. A Microsoft tárolókatalógus című cikk további részleteket tartalmaz.

Ha a Dockert a .NET-feldolgozó szolgáltatás üzembehelyezési stratégiájaként célozza meg, a projektfájlban néhány szempont szerepel:

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

  <PropertyGroup>
    <TargetFramework>net10.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.7" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
  </ItemGroup>
</Project>

Az előző projektfájlban a <DockerDefaultTargetOS> elem a Linux határozza meg célként. Inkább a Windows-tartályok célzásához használja a Windows. A Microsoft.VisualStudio.Azure.Containers.Tools.Targets NuGet-csomag automatikusan hozzáadódik csomaghivatkozásként, amikor a sablonból a Docker-támogatás kerül kiválasztásra.

A Dockerrel és a .NET-tel kapcsolatos további információkért lásd oktatóanyagot: .NET-alkalmazás tárolóba helyezése. Az Azure-ban való üzembe helyezésről további információt a oktatóanyagban talál: Feldolgozó szolgáltatás üzembe helyezése az Azure-ban.

Fontos

Ha a Felhasználói titkok funkciót szeretné használni a Worker sablonnal, akkor kifejezetten hivatkoznia kell a Microsoft.Extensions.Configuration.UserSecrets NuGet-csomagra.

Üzemeltetett szolgáltatás bővíthetősége

A IHostedService felület két módszert határoz meg:

Ez a két módszer életciklus- metódusként szolgál – ezeket a host indítási és leállítási eseményei során hívjuk meg.

Jegyzet

A StartAsync vagy StopAsync metódusok felülírásakor meg kell hívnia és await a base osztálymetódust, hogy a szolgáltatás megfelelően elinduljon és/vagy leálljon.

Fontos

Az interfész általános típusú paraméterkorlátként szolgál a AddHostedService<THostedService>(IServiceCollection) bővítménymetódushoz, ami azt jelenti, hogy csak implementációk engedélyezettek. Használhatja a megadott BackgroundService-t egy alosztállyal, vagy teljesen megvalósíthatja a sajátját.

Jelzés befejezése

A leggyakoribb forgatókönyvekben nem kell explicit módon jeleznie egy üzemeltetett szolgáltatás befejezését. Amikor a gazdagép elindítja a szolgáltatásokat, úgy vannak kialakítva, hogy addig fussanak, amíg a gazdagép le nem áll. Bizonyos esetekben azonban előfordulhat, hogy a szolgáltatás befejeződésekor a teljes gazdaalkalmazás befejezését kell jeleznie. A befejezés jelzéséhez vegye figyelembe a következő Worker osztályt:

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

Az előző kódban a BackgroundService.ExecuteAsync(CancellationToken) metódus nem ismétlődik, és ha elkészült, meghívja a IHostApplicationLifetime.StopApplication()metódust.

Fontos

Ez jelzi a gazdagépnek, hogy le kell állnia, és a StopApplication hívás nélkül a gazdagép továbbra is határozatlan ideig fog futni. Ha rövid időre szóló üzemeltetett szolgáltatást szeretne futtatni (egyszeri futtatási alkalommal), és a Worker sablont szeretné használni, akkor fel kell hívnia a StopApplication függvényt a gazdagép leállításához.

További információ:

Alternatív megközelítés

A függőségi injekciót, naplózást és konfigurációt igénylő rövid életű alkalmazásokhoz használja a Worker sablon helyett a .NET Általános gazdagépet. Így az osztály nélkül is használhatja ezeket a Worker funkciókat. Az általános gazdagépet használó rövid élettartamú alkalmazások egyszerű példája az alábbihoz hasonló projektfájlt definiálhat:

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

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
    <RootNamespace>ShortLived.App</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
  </ItemGroup>
</Project>

Program Az osztály a következőhöz hasonlóan nézhet ki:

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
}

Az előző kód létrehoz egy JobRunner szolgáltatást, amely egy egyéni osztály, amely tartalmazza a feladat futtatásához szükséges logikát. A RunAsync metódus meghívódik a JobRunner-en, és ha sikeresen befejeződik, az alkalmazás visszaadja 0. Ha nem kezelt kivétel történik, naplózza a hibát, és visszaadja 1.

Ebben az egyszerű forgatókönyvben az osztály a JobRunner következőképpen nézhet ki:

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

Nyilvánvalóan valódi logikát kell hozzáadnia a RunAsync metódushoz, de ez a példa bemutatja, hogyan használhatja a generikus gazdagépet rövid élettartamú alkalmazásokhoz anélkül, hogy szükség lenne egy Worker osztályra, és anélkül, hogy explicit módon jelezni kellene a gazdagép befejezését.

Lásd még: