Службы рабочих ролей в .NET
Существует множество причин для создания длительно выполняемых служб, такие как:
- Обработка данных с большим объемом ЦП.
- поочередная работа с рабочими элементами в фоновом режиме;
- выполнение операции ограниченное время по расписанию.
Работа фоновых служб обычно не требует пользовательского интерфейса, но пользовательские интерфейсы для них их можно создать. В первые дни с платформа .NET Framework разработчики Windows могут создавать службы Windows для этих целей. Теперь с .NET можно использовать BackgroundServiceреализацию IHostedServiceили реализовать собственную.
В .NET вы больше не ограничены средой Windows. Можно разрабатывать кроссплатформенные фоновые службы. Размещенные службы позволяют реализовать журналирование, конфигурирование и внедрение зависимостей. Они являются частью набора расширений библиотек, что означает, что они являются фундаментальными для всех рабочих нагрузок .NET, работающих с универсальным узлом.
Внимание
Установка пакета SDK для .NET также устанавливает Microsoft.NET.Sdk.Worker
и шаблон рабочей роли. Другими словами, после установки пакета SDK для .NET можно создать рабочую роль с помощью команды dotnet new worker . Если вы используете Visual Studio, шаблон скрыт до установки необязательной ASP.NET и рабочей нагрузки веб-разработки.
Терминология
Существует множество терминов, которые по ошибке используются как синонимы. Этот раздел определяет некоторые из этих терминов, чтобы сделать их намерение в этой статье более очевидным.
- Фоновая BackgroundService служба: тип.
- Размещенная служба: реализация IHostedServiceили IHostedService сама по себе.
- Долго выполняемая служба: любая служба, которая работает постоянно.
- Служба Windows: инфраструктура службы Windows, изначально платформа .NET Framework ориентированная, но теперь доступна через .NET.
- Рабочая служба: шаблон службы рабочей роли .
Шаблон службы рабочей роли
Шаблон рабочей службы доступен в .NET CLI и Visual Studio. Дополнительные сведения см. в обзоре командной строки .NETdotnet 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
, который запускает приложение.
Параметры шаблона по умолчанию
Шаблон рабочей роли не включает сборку мусора сервера (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
Приложение на основе шаблона рабочей роли использует Microsoft.NET.Sdk.Worker
пакет SDK и имеет явную ссылку на пакет 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
означает "Microsoft Container Registry", а также является каталогом для выобъединенного контейнера Майкрософт из официального центра Docker. Дополнительные сведения см. в статье Объединение каталога контейнеров Microsoft.
При целевом использовании 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="8.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
В предыдущем файле проекта элемент <DockerDefaultTargetOS>
указывает Linux
в качестве целевого объекта. Чтобы выбрать контейнеры Windows, используйте Windows
вместо этого. Microsoft.VisualStudio.Azure.Containers.Tools.Targets
Пакет NuGet автоматически добавляется в качестве ссылки на пакет, если в шаблоне выбрана поддержка Docker.
Дополнительные сведения о поддержке Docker в .NET см. в учебнике по контейнеризации приложения .NET. Дополнительные сведения о развертывании в Azure см. в разделе Учебник. Развертывание службы рабочей роли в Azure.
Внимание
Если вы хотите использовать секреты пользователей с шаблоном рабочей роли, необходимо явно ссылаться на Microsoft.Extensions.Configuration.UserSecrets
пакет NuGet.
Расширяемость размещенной службы
Интерфейс IHostedService определяет два метода:
Эти два метода служат в качестве методов жизненного цикла — они вызываются во время событий запуска и останова узла, соответственно.
Примечание.
При переопределении методов StartAsync или StopAsync вы должны вызвать метод класса base
и выполнить await
, чтобы обеспечить правильный запуск и (или) завершение работы службы.
Внимание
Интерфейс выступает в качестве ограничения параметра универсального типа для метода расширения 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: завершение работы узла
- Универсальный узел .NET: процесс завершения работы размещения