Usługi robocze na platformie .NET
Istnieje wiele powodów tworzenia długotrwałych usług, takich jak:
- Przetwarzanie danych intensywnie korzystających z procesora CPU.
- Kolejkowanie elementów roboczych w tle.
- Wykonywanie operacji opartej na czasie zgodnie z harmonogramem.
Przetwarzanie usługi w tle zwykle nie obejmuje interfejsu użytkownika, ale interfejsy użytkownika można tworzyć wokół nich. We wczesnych dniach .NET Framework deweloperzy systemu Windows mogą tworzyć usługi systemu Windows z tych powodów. Teraz przy użyciu platformy .NET możesz użyć elementu BackgroundService, który jest implementacją IHostedService, lub zaimplementować własny.
Platforma .NET nie jest już ograniczona do systemu Windows. Możesz tworzyć międzyplatformowe usługi w tle. Hostowane usługi są gotowe do rejestrowania, konfiguracji i wstrzykiwania zależności (DI). Są one częścią zestawu rozszerzeń bibliotek, co oznacza, że są one podstawowe dla wszystkich obciążeń platformy .NET, które współpracują z hostem ogólnym.
Ważne
Zainstalowanie zestawu .NET SDK powoduje również zainstalowanie Microsoft.NET.Sdk.Worker
szablonu i procesu roboczego. Innymi słowy, po zainstalowaniu zestawu SDK platformy .NET można utworzyć nowy proces roboczy przy użyciu polecenia dotnet new worker . Jeśli używasz programu Visual Studio, szablon jest ukryty do momentu zainstalowania opcjonalnego obciążenia ASP.NET i tworzenia aplikacji internetowych.
Terminologia
Wiele terminów jest błędnie używanych synonimem. W tej sekcji istnieją definicje niektórych z tych terminów, aby ich intencja była bardziej widoczna.
- Usługa w tle: odwołuje się do BackgroundService typu.
- Hostowana usługa: implementacje IHostedServiceelementu lub odwołujące się do IHostedService samego siebie.
- Długotrwała usługa: Każda usługa, która działa w sposób ciągły.
- Usługa systemu Windows: infrastruktura usługi systemu Windows, pierwotnie .NET Framework skoncentrowana, ale teraz dostępna za pośrednictwem platformy .NET.
- Usługa procesu roboczego: odwołuje się do szablonu usługi procesu roboczego .
Szablon usługi procesu roboczego
Szablon usługi Worker Service jest dostępny dla interfejsu wiersza polecenia platformy .NET i programu Visual Studio. Aby uzyskać więcej informacji, zobacz interfejs wiersza polecenia platformy .NET, dotnet new worker
— template. Szablon składa się z Program
klasy i Worker
.
using App.WorkerService;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
Poprzednia Program
klasa:
- Tworzy wartość domyślną IHostBuilder.
- Wywołania ConfigureServices w celu dodania
Worker
klasy jako usługi hostowanej za pomocą polecenia AddHostedService. - Tworzy element IHost na podstawie konstruktora.
host
WywołujeRun
wystąpienie, które uruchamia aplikację.
Porada
Domyślnie szablon usługi procesu roboczego nie włącza odzyskiwania pamięci serwera (GC). Wszystkie scenariusze wymagające długotrwałych usług powinny uwzględniać wpływ na wydajność tego ustawienia domyślnego. Aby włączyć GC serwera, dodaj ServerGarbageCollection
węzeł do pliku projektu:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
Aby uzyskać więcej informacji na temat zagadnień dotyczących wydajności, zobacz Serwer GC. Aby uzyskać więcej informacji na temat konfigurowania GC serwera, zobacz Przykłady konfiguracji GC serwera.
Plik Program.cs z szablonu można przepisać przy użyciu instrukcji najwyższego poziomu:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using App.WorkerService;
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
Jest to funkcjonalnie równoważne oryginalnemu szablonowi. Aby uzyskać więcej informacji na temat funkcji języka C# 9, zobacz Co nowego w języku C# 9.0.
Jeśli chodzi o Worker
element , szablon zapewnia prostą implementację.
namespace App.WorkerService
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
}
Poprzednia Worker
klasa jest podklasą BackgroundServiceklasy , która implementuje IHostedServiceelement . Element BackgroundService jest elementem abstract class
i wymaga, aby podklasa zaimplementowała BackgroundService.ExecuteAsync(CancellationToken)element . W implementacji szablonu ExecuteAsync
pętle raz na sekundę rejestrują bieżącą datę i godzinę do momentu zasygnalizowania anulowania procesu.
Plik projektu
Szablon usługi Worker Service opiera się na następującym pliku Sdk
projektu:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Aby uzyskać więcej informacji, zobacz Zestawy SDK projektu .NET.
Pakiet NuGet
Aplikacja oparta na szablonie usługi procesu roboczego używa Microsoft.NET.Sdk.Worker
zestawu SDK i ma jawne odwołanie do pakietu Microsoft.Extensions.Hosting .
Kontenery i możliwości dostosowywania chmury
W przypadku większości nowoczesnych obciążeń platformy .NET kontenery są realną opcją. Podczas tworzenia długotrwałej usługi na podstawie szablonu usługi procesu roboczego w programie Visual Studio możesz wyrazić zgodę na pomoc techniczną platformy Docker. Spowoduje to utworzenie pliku Dockerfile , który będzie konteneryzować aplikację .NET. Plik Dockerfile to zestaw instrukcji tworzenia obrazu. W przypadku aplikacji .NET plik Dockerfile zwykle znajduje się w katalogu głównym katalogu obok pliku rozwiązania.
# 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:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.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"]
Powyższe kroki pliku Dockerfile obejmują:
- Ustawienie obrazu podstawowego z
mcr.microsoft.com/dotnet/runtime:6.0
jako aliasubase
. - Zmiana katalogu roboczego na /app.
- Ustawianie aliasu
build
zmcr.microsoft.com/dotnet/sdk:6.0
obrazu. - Zmiana katalogu roboczego na /src.
- Kopiowanie zawartości i publikowanie aplikacji .NET:
- Aplikacja jest publikowana przy użyciu
dotnet publish
polecenia .
- Aplikacja jest publikowana przy użyciu
- Przekazywanie obrazu zestawu SDK platformy .NET z
mcr.microsoft.com/dotnet/runtime:6.0
(aliasubase
). - Kopiowanie opublikowanych danych wyjściowych kompilacji z /publish.
- Definiowanie punktu wejścia, który deleguje do elementu
dotnet App.BackgroundService.dll
.
Porada
McR oznacza mcr.microsoft.com
"Microsoft Container Registry" i jest syndykalizatorem kontenerów firmy Microsoft z oficjalnego centrum Platformy Docker. Artykuł dotyczący wykazu kontenerów syndykates firmy Microsoft zawiera dodatkowe szczegóły.
W przypadku określania celu platformy Docker jako strategii wdrażania dla usługi procesów roboczych platformy .NET należy wziąć pod uwagę kilka zagadnień w pliku projektu:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WorkerService</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />
</ItemGroup>
</Project>
W poprzednim pliku <DockerDefaultTargetOS>
projektu element określa jako element docelowy Linux
. Zamiast tego należy użyć kontenerów Windows
systemu Windows. PakietMicrosoft.VisualStudio.Azure.Containers.Tools.Targets
NuGet jest automatycznie dodawany jako odwołanie do pakietu po wybraniu obsługi platformy Docker z szablonu.
Aby uzyskać więcej informacji na temat platformy Docker z platformą .NET, zobacz Samouczek: konteneryzowanie aplikacji .NET. Aby uzyskać więcej informacji na temat wdrażania na platformie Azure, zobacz Samouczek: wdrażanie usługi procesu roboczego na platformie Azure.
Ważne
Jeśli chcesz korzystać z wpisów tajnych użytkownika za pomocą szablonu usługi procesu roboczego, musisz jawnie odwołać Microsoft.Extensions.Configuration.UserSecrets
się do pakietu NuGet.
Rozszerzalność usługi hostowanej
Interfejs IHostedService definiuje dwie metody:
Te dwie metody służą jako metody cyklu życia — są one wywoływane odpowiednio podczas uruchamiania i zatrzymywania zdarzeń hosta.
Uwaga
Podczas zastępowania metod StartAsync lub StopAsync należy wywołać metodę i await
metodę base
klasy, aby upewnić się, że usługa jest uruchamiana i/lub zamyka się prawidłowo.
Ważne
Interfejs służy jako ograniczenie parametru typu ogólnego w metodzie AddHostedService<THostedService>(IServiceCollection) rozszerzenia, co oznacza, że dozwolone są tylko implementacje. Możesz korzystać z dostarczonej BackgroundService z podklasą lub całkowicie zaimplementować własną.
Uzupełnianie sygnału
W większości typowych scenariuszy nie trzeba jawnie sygnalizować ukończenia hostowanej usługi. Po uruchomieniu usług host jest przeznaczony do uruchamiania do momentu zatrzymania hosta. Jednak w niektórych scenariuszach może być konieczne sygnalizowanie ukończenia całej aplikacji hosta po zakończeniu pracy usługi. Aby to osiągnąć, rozważmy następującą Worker
klasę:
namespace App.SignalCompletionService;
public class Worker : BackgroundService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly ILogger<Worker> _logger;
public Worker(
IHostApplicationLifetime hostApplicationLifetime, ILogger<Worker> logger) =>
(_hostApplicationLifetime, _logger) = (hostApplicationLifetime, logger);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// TODO: implement single execution logic here.
_logger.LogInformation(
"Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
// When completed, the entire app host will stop.
_hostApplicationLifetime.StopApplication();
}
}
W poprzednim kodzie ExecuteAsync
metoda nie wykonuje pętli, a po jej zakończeniu wywołuje metodę IHostApplicationLifetime.StopApplication().
Ważne
Będzie to sygnalizować hostowi, że powinien zostać zatrzymany, a bez tego wywołania hosta StopApplication
będzie nadal działać w nieskończoność.
Aby uzyskać więcej informacji, zobacz:
- Host ogólny platformy .NET: IHostApplicationLifetime
- Host ogólny platformy .NET: zamykanie hosta
- Host ogólny platformy .NET: proces zamykania hostingu
Zobacz też
- BackgroundService samouczki dotyczące podklas:
- Implementacja niestandardowa IHostedService :