.NET의 작업자 서비스

장기 실행 서비스를 만드는 데는 다음과 같은 여러 가지 이유가 있습니다.

  • CPU 집약적 데이터 처리.
  • 백그라운드에서 작업 항목 큐.
  • 일정에 따라 시간 기준 작업 수행.

백그라운드 서비스 처리에는 일반적으로 UI(사용자 인터페이스)가 포함되지 않지만 UI를 기반으로 빌드할 수 있습니다. .NET Framework 초기에는 Windows 개발자가 이러한 이유로 Windows Services를 만들 수 있었습니다. 이제 .NET을 사용하여 IHostedService(을)를 구현하는 BackgroundService(을)를 사용하거나 직접 구현할 수 있습니다.

.NET을 사용하면 더 이상 Windows에 제한되지 않습니다. 플랫폼 간 백그라운드 서비스를 개발할 수 있습니다. 호스트된 서비스는 로깅, 구성 및 DI(종속성 주입)를 준비합니다. 라이브러리 확장 모음의 일부로, 이는 제네릭 호스트에서 작동하는 모든 .NET 워크로드의 기본 사항임을 의미합니다.

Important

.NET SDK를 설치하면 Microsoft.NET.Sdk.Worker 및 작업자 템플릿도 설치됩니다. 즉, .NET SDK를 설치한 후 dotnet new Worker 명령을 사용하여 새 작업자를 만들 수 있습니다. Visual Studio를 사용하는 경우 선택적 ASP.NET 및 웹 개발 워크로드가 설치될 때까지 템플릿이 숨겨집니다.

용어

많은 용어가 동의어로 잘못 사용됩니다. 이 섹션에는 이러한 용어의 의도를 더 명확하게 하기 위한 정의가 나와 있습니다.

  • 백그라운드 서비스: BackgroundService 형식을 참조합니다.
  • 호스트된 서비스: IHostedService의 구현 또는 IHostedService 자체를 참조합니다.
  • 장기 실행 서비스: 지속적으로 실행되는 모든 서비스입니다.
  • Windows Service: Windows Service 인프라는 원래 .NET Framework 중심이지만 이제는 .NET을 통해 액세스할 수 있습니다.
  • Worker Service: Worker Service 템플릿을 참조합니다.

Worker Service 템플릿

Worker Service 템플릿은 .NET CLI 및 Visual Studio에서 사용할 수 있습니다. 자세한 내용은 .NET CLI, dotnet new worker - 템플릿을 참조하세요. 템플릿은 ProgramWorker 클래스로 구성됩니다.

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(서버 가비지 수집)를 사용하도록 설정하지 않습니다. 그 필요성을 결정하는 데 중요한 역할을 하는 여러 요소가 있기 때문입니다. 장기 실행 서비스가 필요한 모든 시나리오는 이 기본값의 성능 영향을 고려해야 합니다. 서버 GC를 사용하도록 설정하려면 프로젝트 파일에 ServerGarbageCollection 노드를 추가합니다.

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

절충 및 고려 사항

사용 사용 안 함
효율적인 메모리 관리: 메모리 누수 방지 및 리소스 사용 최적화를 위해 사용되지 않는 메모리를 자동으로 회수합니다. 실시간 성능 향상: 대기 시간에 민감한 애플리케이션에서 가비지 수집으로 인한 잠재적 일시 중지 또는 중단을 방지합니다.
장기 안정성: 오랜 기간 동안 메모리를 관리하여 장기 실행 서비스에서 안정적인 성능을 유지하는 데 도움이 됩니다. 리소스 효율성: 리소스가 제한된 환경에서 CPU 및 메모리 리소스를 절약할 수 있습니다.
유지 관리 감소: 수동 메모리 관리의 필요성을 최소화하여 유지 관리를 간소화합니다. 수동 메모리 제어: 특수 애플리케이션에 대한 메모리에 대한 세분화된 제어를 제공합니다.
예측 가능한 동작: 일관되고 예측 가능한 애플리케이션 동작에 기여합니다. 단기 프로세스에 적합: 단기 또는 임시 프로세스에 대한 가비지 수집 오버헤드를 최소화합니다.

성능 고려 사항에 대한 자세한 내용은 Server GC를 참조하세요. 서버 GC를 구성하는 방법에 대한 자세한 내용은 Server 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 클래스는 IHostedService를 구현하는 BackgroundService의 하위 클래스입니다. BackgroundServiceabstract class이며 BackgroundService.ExecuteAsync(CancellationToken)을 구현하려면 하위 클래스가 필요합니다. 템플릿 구현에서 ExecuteAsync(은)는 초당 한 번씩 반복되며 프로세스가 취소하라는 신호를 표시할 때까지 현재 날짜 및 시간을 기록합니다.

프로젝트 파일

작업자 템플릿은 다음 프로젝트 파일 Sdk(을)를 사용합니다.

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

자세한 내용은 .NET 프로젝트 SDK를 참조하세요.

NuGet 패키지

작업자 템플릿을 기반으로 하는 앱은 Microsoft.NET.Sdk.Worker SDK를 사용하며 Microsoft.Extensions.Hosting 패키지에 대한 명시적 패키지 참조가 있습니다.

컨테이너 및 클라우드 적응성

대부분의 최신 .NET 워크로드에서는 컨테이너가 실행 가능한 옵션입니다. Visual Studio의 작업자 템플릿에서 장기 실행 서비스를 만들 때 Docker 지원으로 옵트인할 수 있습니다. 이렇게 하면 .NET 앱을 컨테이너화하는 Dockerfile이 만들어집니다. 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으로 변경합니다.
  • mcr.microsoft.com/dotnet/sdk:8.0 이미지에서 별칭 build를 설정합니다.
  • 작업 디렉터리를 /src로 변경합니다.
  • 콘텐츠를 복사하고 .NET 앱을 게시합니다.
  • mcr.microsoft.com/dotnet/runtime:8.0(별칭 base)에서 .NET SDK 이미지를 릴레이합니다.
  • /publish에서 게시된 빌드 출력을 복사합니다.
  • dotnet App.BackgroundService.dll에 위임하는 진입점을 정의합니다.

mcr.microsoft.com의 MCR은 “Microsoft Container Registry”의 약자이며 Microsoft가 배포하는 공식 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를 대신 사용합니다. 템플릿에서 Docker 지원을 선택하면 Microsoft.VisualStudio.Azure.Containers.Tools.Targets NuGet 패키지가 패키지 참조로 자동으로 추가됩니다.

.NET을 사용한 Docker에 대한 자세한 내용은 자습서: .NET 앱 컨테이너화를 참조하세요. Azure에 배포하는 자세한 내용은 자습서: Azure에 Worker Service 배포를 참조하세요.

Important

작업자 템플릿을 사용하여 사용자 비밀을 활용하려면 Microsoft.Extensions.Configuration.UserSecrets NuGet 패키지를 명시적으로 참조해야 합니다.

호스트된 서비스 확장성

IHostedService 인터페이스는 다음 두 가지 메서드를 정의합니다.

이러한 두 메서드는 수명 주기 메서드 역할을 합니다. 호스트 시작 및 중지 이벤트 중에 각각 호출됩니다.

참고 항목

StartAsync 또는 StopAsync 메서드를 재정의하는 경우 awaitbase 클래스 메서드를 호출하여 서비스가 제대로 시작 및/또는 종료되도록 해야 합니다.

Important

인터페이스는 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()(을)를 호출합니다.

Important

그러면 호스트가 중지되어야 한다는 신호가 표시되고, StopApplication 대한 이 호출이 없으면 호스트가 무기한으로 실행됩니다.

자세한 내용은 다음을 참조하세요.

참고 항목