다음을 통해 공유


IHostedService 및 BackgroundService 클래스를 사용하여 마이크로 서비스에서 백그라운드 작업 구현

팁 (조언)

이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.

컨테이너화된 .NET 애플리케이션을 위한 .NET 마이크로서비스 아키텍처 eBook의 표지 썸네일.

백그라운드 작업 및 예약된 작업은 마이크로 서비스 아키텍처 패턴을 따르는지 여부에 관계없이 모든 애플리케이션에서 사용해야 할 수 있습니다. 마이크로 서비스 아키텍처를 사용할 때의 차이점은 호스팅을 위한 별도의 프로세스/컨테이너에서 백그라운드 작업을 구현하여 필요에 따라 축소/축소할 수 있다는 것입니다.

일반적인 관점에서 .NET에서는 호스트/애플리케이션/마이크로 서비스 내에서 호스트하는 서비스/논리이기 때문에 이러한 유형의 태스크 Hosted Services를 호출했습니다. 이 경우 호스팅된 서비스는 백그라운드 작업 논리가 있는 클래스를 의미합니다.

.NET Core 2.0부터 프레임워크는 호스트된 서비스를 쉽게 구현하는 데 도움이 되는 새 IHostedService 인터페이스를 제공합니다. 기본 개념은 이미지 6-26과 같이 웹 호스트 또는 호스트가 실행되는 동안 백그라운드에서 실행되는 여러 백그라운드 작업(호스팅된 서비스)을 등록할 수 있다는 것입니다.

ASP.NET Core IWebHost 및 .NET Core IHost를 비교하는 다이어그램

그림 6-26. WebHost와 일반 호스트에서 IHostedService 사용

ASP.NET Core 1.x 및 2.x는 웹앱의 백그라운드 프로세스를 지원 IWebHost 합니다. .NET Core 2.1 이상 버전은 일반 콘솔 앱을 사용하는 백그라운드 프로세스를 IHost를 지원합니다. WebHostHost의 차이점을 주의하세요.

ASP.NET Core 2.0의 A WebHost (기본 클래스 구현 IWebHost)는 MVC 웹앱 또는 Web API 서비스를 구현하는 경우와 같이 프로세스에 HTTP 서버 기능을 제공하는 데 사용하는 인프라 아티팩트입니다. ASP.NET Core의 모든 새로운 인프라 기능을 제공하므로 종속성 주입을 사용하고 요청 파이프라인에 미들웨어를 삽입할 수 있습니다. 백그라운드 작업에는 WebHost 이와 동일하게 IHostedServices 사용됩니다.

A Host (기본 클래스 구현 IHost)는 .NET Core 2.1에서 도입되었습니다. 기본적으로는 Host 종속성 주입, 호스팅 서비스 등과 비슷한 인프라 WebHost 를 사용할 수 있지만, 이 경우 MVC, Web API 또는 HTTP 서버 기능과 아무런 관련이 없는 간단하고 가벼운 프로세스를 호스트로 사용하려고 합니다.

따라서 호스트된 서비스만 처리할 수 있는 특수 호스트 프로세스를 IHost 선택하여 만들거나, IHostedServices만 호스팅을 목적으로 설계된 마이크로서비스를 선택할 수 있으며, 또는 기존 ASP.NET Core Web API나 MVC 앱과 같은 ASP.NET Core WebHost를 확장하는 방법도 있습니다.

각 접근 방식에는 비즈니스 및 확장성 요구 사항에 따라 장단점이 있습니다. 결론은 기본적으로 백그라운드 작업이 HTTP(IWebHost)와 아무 상관이 없는 경우 사용해야 IHost한다는 것입니다.

웹 호스트 또는 호스트에 호스팅된 서비스 등록

IHostedService 인터페이스는 사용법이 WebHostHost에서 매우 유사하므로 좀 더 자세히 살펴보겠습니다.

SignalR은 호스트된 서비스를 사용하는 아티팩트 예제 중 하나이지만 다음과 같은 훨씬 간단한 작업에 사용할 수도 있습니다.

  • 변경 내용을 찾는 데이터베이스를 폴링하는 백그라운드 작업입니다.
  • 일부 캐시를 주기적으로 업데이트하는 예약된 작업입니다.
  • 백그라운드 스레드에서 작업을 실행할 수 있도록 하는 QueueBackgroundWorkItem의 구현입니다.
  • 웹앱의 백그라운드에서 메시지 큐의 메시지를 처리하면서 ILogger과 같은 일반적인 서비스를 공유합니다.
  • 백그라운드 작업이 Task.Run()로 시작되었습니다.

기본적으로 이러한 작업을 구현하는 백그라운드 작업으로 오프로드할 수 있습니다 IHostedService.

하나 이상의 IHostedServicesWebHost 또는 Host에 추가하려면 ASP.NET Core AddHostedService (또는 .NET Core 2.1 이상에서는 WebHost)의 Host 확장 메서드를 통해 등록해야 합니다. 기본적으로 Program.cs 애플리케이션 시작 내에서 호스트된 서비스를 등록해야 합니다.

//Other DI registrations;

// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...

이 코드 GracePeriodManagerService 에서 호스팅된 서비스는 eShopOnContainers의 Ordering Business 마이크로 서비스의 실제 코드이며, 나머지 두 가지는 두 개의 추가 샘플일 뿐입니다.

IHostedService 백그라운드 작업 실행은 애플리케이션(호스트 또는 마이크로서비스)의 수명과 조정됩니다. 애플리케이션이 시작될 때 작업을 등록하고 애플리케이션을 종료할 때 몇 가지 정상적인 작업을 수행하거나 정리할 수 있습니다.

사용하지 IHostedService않고 항상 백그라운드 스레드를 시작하여 작업을 실행할 수 있습니다. 차이점은 정상적인 정리 작업을 실행할 기회를 갖지 않고 해당 스레드가 단순히 종료될 때 앱의 종료 시간에 정확하게 적용됩니다.

IHostedService 인터페이스

애플리케이션을 시작할 때 및 중지할 때 각각 .NET은 IHostedService를 등록하면 StartAsync() 형식의 StopAsync() 메서드와 IHostedService 메서드를 호출합니다. 자세한 내용은 IHostedService 인터페이스를 참조하세요.

앞에서 설명한 것처럼 IHostedService의 여러 구현을 만들고 각 구현을 Program.cs 등록할 수 있습니다. 이러한 모든 호스팅 서비스는 애플리케이션/마이크로 서비스와 함께 시작 및 중지됩니다.

개발자는 호스트에 의해 메서드가 트리거될 때 StopAsync() 서비스의 중지 작업을 처리해야 합니다.

BackgroundService 기본 클래스에서 파생된 사용자 지정 호스팅 서비스 클래스를 사용하여 IHostedService 구현

.NET Core 2.0 이상을 사용할 때 필요로 하는 경우처럼, 사용자 지정 호스팅 서비스 클래스를 처음부터 계속해서 만들고 IHostedService 구현할 수 있습니다.

그러나 대부분의 백그라운드 작업에는 취소 토큰 관리 및 기타 일반적인 작업과 관련하여 비슷한 요구 사항이 있으므로 명명된( BackgroundService .NET Core 2.1 이후 사용 가능)에서 파생할 수 있는 편리한 추상 기본 클래스가 있습니다.

이 클래스는 백그라운드 작업을 설정하는 데 필요한 기본 작업을 제공합니다.

다음 코드는 .NET에서 구현된 추상 BackgroundService 기본 클래스입니다.

// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                          cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

이전 추상 기본 클래스에서 파생할 때, 상속된 구현 덕분에 단순히 사용자 고유의 사용자 지정 호스팅 서비스 클래스에서 ExecuteAsync() 메서드를 구현하기만 하면 됩니다. 다음은 eShopOnContainers의 간소화된 코드 예시로, 데이터베이스를 폴링하고 필요한 경우 Event Bus에 통합 이벤트를 게시하는 역할을 합니다.

public class GracePeriodManagerService : BackgroundService
{
    private readonly ILogger<GracePeriodManagerService> _logger;
    private readonly OrderingBackgroundSettings _settings;

    private readonly IEventBus _eventBus;

    public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
                                     IEventBus eventBus,
                                     ILogger<GracePeriodManagerService> logger)
    {
        // Constructor's parameters validations...
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogDebug($"GracePeriodManagerService is starting.");

        stoppingToken.Register(() =>
            _logger.LogDebug($" GracePeriod background task is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogDebug($"GracePeriod task doing background work.");

            // This eShopOnContainers method is querying a database table
            // and publishing events into the Event Bus (RabbitMQ / ServiceBus)
            CheckConfirmedGracePeriodOrders();

            try {
                    await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
                }
            catch (TaskCanceledException exception) {
                    _logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
                }
        }

        _logger.LogDebug($"GracePeriod background task is stopping.");
    }

    .../...
}

eShopOnContainers에 대한 이 특정 사례에서는 특정 상태의 주문을 찾는 데이터베이스 테이블을 쿼리하는 애플리케이션 메서드를 실행하고 변경 내용을 적용할 때 이벤트 버스를 통해 통합 이벤트를 게시합니다(그 아래에 RabbitMQ 또는 Azure Service Bus를 사용할 수 있습니다).

물론 다른 비즈니스 백그라운드 작업을 대신 실행할 수 있습니다.

기본적으로 취소 토큰은 5초 시간 제한으로 설정되지만, WebHost 확장을 사용하여 UseShutdownTimeout 빌드 시 IWebHostBuilder의 값을 변경할 수 있습니다. 즉, 서비스가 5초 이내에 취소될 것으로 예상되며, 그렇지 않으면 더 갑자기 종료됩니다.

다음 코드는 해당 시간을 10초로 변경합니다.

WebHost.CreateDefaultBuilder(args)
    .UseShutdownTimeout(TimeSpan.FromSeconds(10))
    ...

요약 클래스 다이어그램

다음 이미지는 IHostedServices를 구현할 때 관련된 클래스 및 인터페이스의 시각적 요약을 보여 줍니다.

IWebHost 및 IHost가 많은 서비스를 호스트할 수 있음을 보여 주는 다이어그램

그림 6-27. IHostedService와 관련된 여러 클래스 및 인터페이스를 보여 주는 클래스 다이어그램

클래스 다이어그램: IWebHost 및 IHost는 IHostedService를 구현하는 BackgroundService에서 상속되는 많은 서비스를 호스트할 수 있습니다.

배포 고려 사항 및 설명

ASP.NET Core WebHost 또는 .NET Host 을 배포하는 방식이 최종 솔루션에 영향을 미칠 수 있다는 점에 유의해야 합니다. 예를 들어 IIS 또는 Azure App Service에 WebHost을(를) 배포하는 경우 앱 풀 재활용으로 인해 호스트가 종료될 수 있습니다. 그러나 Kubernetes와 같은 오케스트레이터에 호스트를 컨테이너로 배포하는 경우 호스트의 보장된 라이브 인스턴스 수를 제어할 수 있습니다. 또한 Azure Functions와 같은 이러한 시나리오에 대해 특별히 만들어진 클라우드의 다른 접근 방식을 고려할 수 있습니다. 마지막으로, 서비스를 항상 실행해야 하고 Windows Server에 배포하는 경우 Windows 서비스를 사용할 수 있습니다.

그러나 앱 풀에 배포된 경우에도 WebHost 애플리케이션의 메모리 내 캐시를 다시 채우거나 플러시하는 등의 시나리오는 여전히 적용할 수 있습니다.

이 인터페이스는 IHostedService ASP.NET Core 웹 애플리케이션(.NET Core 2.0 이상 버전) 또는 모든 프로세스/호스트(.NET Core 2.1 IHost부터 시작)에서 백그라운드 작업을 시작하는 편리한 방법을 제공합니다. 주요 이점은 호스트 자체가 종료될 때 백그라운드 작업의 코드를 깔끔하게 정리할 수 있는 우아한 취소 기능을 통해 제공되는 기회입니다.

추가 리소스