Рабочие службы в .NET
Существует множество причин для создания долго работающих сервисов, например:
- Обработка данных с большим объемом ЦП.
- Постановка рабочих элементов в очередь в фоновом режиме.
- Выполнение периодической операции по расписанию.
Фоновая обработка служб обычно не включает пользовательский интерфейс, но интерфейсы могут быть созданы вокруг них. В первые дни с .NET Framework разработчики Windows могут создавать службы Windows для этих целей. Теперь с .NET можно использовать BackgroundService, которая является реализацией IHostedServiceили реализовать собственную.
С помощью .NET вы больше не ограничены Windows. Вы можете разрабатывать кроссплатформенные фоновые службы. Размещенные службы готовы к ведению журнала, настройке и внедрению зависимостей (DI). Они являются частью набора расширений библиотек, то есть они являются основными для всех рабочих нагрузок .NET, которые работают с универсальным узлом.
Важный
Установка пакета SDK для .NET также устанавливает Microsoft.NET.Sdk.Worker
и рабочий шаблон. Другими словами, после установки пакета SDK для .NET можно создать новый worker с помощью команды dotnet new worker. Если вы используете Visual Studio, шаблон скрыт до установки опциональной ASP.NET и компонентов для веб-разработки.
Терминология
Многие термины ошибочно используются синонимами. Этот раздел определяет некоторые из этих терминов, чтобы сделать их намерение в этой статье более очевидным.
- фоновая служба: тип BackgroundService.
- размещенной службы: реализации IHostedServiceили самого IHostedService.
- длительно работающая служба: любая служба, которая выполняется непрерывно.
- Служба Windows: инфраструктура службы Windows, изначально ориентированная на .NET Framework, но теперь доступная через .NET.
- Рабочая служба: шаблон рабочей службы.
Шаблон рабочей службы
Шаблон рабочей службы доступен в .NET CLI и Visual Studio. Дополнительные сведения см. в разделе .NET CLI dotnet new worker
— шаблон. Шаблон состоит из класса Program
и Worker
.
using App.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
Предыдущий класс Program
:
- Создает HostApplicationBuilder.
- Вызывает AddHostedService для регистрации
Worker
в качестве хостинговой службы. - Создает IHost с помощью билдера.
- Вызывает
Run
на экземпляреhost
, который запускает приложение.
Параметры шаблона по умолчанию
Шаблон Worker не включает серверный сборщик мусора (GC) по умолчанию, так как существуют многочисленные факторы, которые влияют на определение его необходимости. Все сценарии, требующие длительно работающих служб, должны учитывать влияние на производительность этого параметра по умолчанию. Чтобы включить серверную сборку GC, добавьте узел ServerGarbageCollection
в файл проекта:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
компромиссы и рекомендации
Включен | Нетрудоспособный |
---|---|
Эффективное управление памятью: автоматически освобождает неиспользуемую память, чтобы предотвратить утечку памяти и оптимизировать использование ресурсов. | Улучшенная производительность в режиме реального времени: предотвращает потенциальные паузы или прерывания, вызванные сборкой мусора в приложениях, чувствительных к задержке. |
Долгосрочная стабильность: помогает поддерживать стабильную производительность в длительных службах, управляя памятью в течение длительных периодов. | Эффективность ресурсов: может сохранять ресурсы ЦП и памяти в средах с ограниченными ресурсами. |
Сокращенное обслуживание: сводит к минимуму потребность в управлении памятью вручную, упрощая обслуживание. | Управление памятью вручную: обеспечивает точное управление памятью для специализированных приложений. |
Прогнозируемое поведение: способствует согласованному и предсказуемому поведению приложения. | Подходит для кратковременных процессов: сводит к минимуму затраты на сборку мусора для кратковременных или временных процессов. |
Дополнительные сведения о вопросах производительности см. в разделе серверного. Дополнительные сведения о настройке GC сервера см. в примерах конфигурации GC сервера.
Рабочий класс
Что касается Worker
, шаблон предоставляет простую реализацию.
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);
}
}
}
Предыдущий Worker
класс является подклассом BackgroundService, который реализует IHostedService.
BackgroundService является abstract class
и требует подкласса для реализации BackgroundService.ExecuteAsync(CancellationToken). В реализации шаблона ExecuteAsync
выполняется один раз в секунду, регистрируя текущее время и дату, пока процесс не будет отменен.
Файл проекта
Шаблон рабочего модуля основан на следующем файле проекта Sdk
:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Дополнительные сведения см. в SDK для проекта .NET.
Пакет NuGet
Приложение, основанное на шаблоне Worker, использует пакет SDK Microsoft.NET.Sdk.Worker
и содержит явную ссылку на пакет Microsoft.Extensions.Hosting.
Контейнеры и облачная адаптация
При использовании большинства современных рабочих нагрузок .NET контейнеры являются жизнеспособным вариантом. При создании длительной службы из шаблона рабочей роли в Visual Studio можно выбрать поддержку Docker. При этом создается Dockerfile, который контейнеризирует приложение .NET. Dockerfile — это набор инструкций по созданию образа. Для приложений .NET Dockerfile обычно находится в корне каталога рядом с файлом решения.
# 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"]
Предшествующие этапы Dockerfile включают:
- Установка базового изображения из
mcr.microsoft.com/dotnet/runtime:8.0
в качестве псевдонимаbase
. - Изменение рабочего каталога на /app.
- Установка псевдонима
build
на основе образаmcr.microsoft.com/dotnet/sdk:8.0
. - Измените рабочий каталог на /src.
- Копирование содержимого и публикация приложения .NET:
- Приложение публикуется с помощью команды
dotnet publish
.
- Приложение публикуется с помощью команды
- Ретрансляция образа пакета SDK для .NET из
mcr.microsoft.com/dotnet/runtime:8.0
(псевдонимbase
). - Копирование опубликованных выходных данных сборки из /publish.
- Определение точки входа, которая делегирует функции
dotnet App.BackgroundService.dll
.
Совет
MCR в mcr.microsoft.com
обозначает "Реестр контейнеров Майкрософт" и является синдикированным каталогом контейнеров Майкрософт из официального центра Docker. Статья каталога контейнеров Microsoft Syndicates содержит дополнительные сведения.
Когда вы выбираете использование Docker в качестве стратегии развертывания для службы рабочего процесса .NET, в файле проекта следует учесть несколько факторов.
<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.3" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
В предыдущем файле проекта элемент <DockerDefaultTargetOS>
указывает Linux
в качестве целевого объекта. Чтобы нацелиться на контейнеры Windows, используйте вместо этого Windows
. Пакет NuGet Microsoft.VisualStudio.Azure.Containers.Tools.Targets
автоматически добавляется в проект в качестве ссылки на пакет, когда из шаблона выбрана поддержка Docker .
Дополнительные сведения о Docker с помощью .NET см. в руководстве по контейнеризации приложения .NET. Дополнительные сведения о развертывании в Azure см. в руководстве по развертыванию рабочей службы в Azure.
Важный
Если вы хотите использовать секреты пользователей с шаблоном Worker, необходимо явно указать пакет NuGet Microsoft.Extensions.Configuration.UserSecrets
.
Расширяемость обслуживаемого сервиса
Интерфейс IHostedService определяет два метода:
Эти два метода служат способами жизненного цикла - они вызываются во время запуска и остановки событий узла соответственно.
Заметка
При переопределении методов StartAsync или StopAsync необходимо вызвать и await
метод класса base
, чтобы убедиться, что служба запускается и /или завершает работу должным образом.
Важный
Интерфейс служит ограничением параметра универсального типа для метода расширения AddHostedService<THostedService>(IServiceCollection), то есть разрешены только реализации. Вы свободно можете использовать предоставленный BackgroundService с подклассом или полностью разработать свой собственный.
Завершение сигнала
В большинстве распространенных сценариев не нужно явно сигнализировать о завершении хостинга-службы. Когда узел запускает службы, они предназначены для работы до остановки узла. Однако в некоторых сценариях может потребоваться сигнал о завершении всего хост-приложения после завершения службы. Чтобы сигнализировать о завершении, рассмотрите следующий класс Worker
:
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();
}
}
В приведенном выше коде метод ExecuteAsync
не выполняет повторений, и, когда он завершает работу, вызывает IHostApplicationLifetime.StopApplication().
Важный
Это сигнализирует хосту о том, что он должен остановиться, и без этого вызова StopApplication
хост будет продолжать работать бесконечно.
Дополнительные сведения см. в следующем разделе:
- общего хоста .NET: IHostApplicationLifetime
- .NET Generic Host: завершение работы универсального узла
- Общий хост .NET: процесс завершения хостинга
См. также
- руководства по подклассам BackgroundService:
- Пользовательская реализация IHostedService: