Службы рабочих ролей в .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 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.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"]

Для 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.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
  </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 узла будет продолжать выполняться неограниченное время.

Дополнительные сведения см. в разделе:

См. также