팁 (조언)
이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.
백그라운드 작업 및 예약된 작업은 마이크로 서비스 아키텍처 패턴을 따르는지 여부에 관계없이 모든 애플리케이션에서 사용해야 할 수 있습니다. 마이크로 서비스 아키텍처를 사용할 때의 차이점은 호스팅을 위한 별도의 프로세스/컨테이너에서 백그라운드 작업을 구현하여 필요에 따라 축소/축소할 수 있다는 것입니다.
일반적인 관점에서 .NET에서는 호스트/애플리케이션/마이크로 서비스 내에서 호스트하는 서비스/논리이기 때문에 이러한 유형의 태스크 Hosted Services를 호출했습니다. 이 경우 호스팅된 서비스는 백그라운드 작업 논리가 있는 클래스를 의미합니다.
.NET Core 2.0부터 프레임워크는 호스트된 서비스를 쉽게 구현하는 데 도움이 되는 새 IHostedService 인터페이스를 제공합니다. 기본 개념은 이미지 6-26과 같이 웹 호스트 또는 호스트가 실행되는 동안 백그라운드에서 실행되는 여러 백그라운드 작업(호스팅된 서비스)을 등록할 수 있다는 것입니다.
그림 6-26. WebHost와 일반 호스트에서 IHostedService 사용
ASP.NET Core 1.x 및 2.x는 웹앱의 백그라운드 프로세스를 지원 IWebHost
합니다. .NET Core 2.1 이상 버전은 일반 콘솔 앱을 사용하는 백그라운드 프로세스를 IHost
를 지원합니다.
WebHost
와 Host
의 차이점을 주의하세요.
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
인터페이스는 사용법이 WebHost
나 Host
에서 매우 유사하므로 좀 더 자세히 살펴보겠습니다.
SignalR은 호스트된 서비스를 사용하는 아티팩트 예제 중 하나이지만 다음과 같은 훨씬 간단한 작업에 사용할 수도 있습니다.
- 변경 내용을 찾는 데이터베이스를 폴링하는 백그라운드 작업입니다.
- 일부 캐시를 주기적으로 업데이트하는 예약된 작업입니다.
- 백그라운드 스레드에서 작업을 실행할 수 있도록 하는 QueueBackgroundWorkItem의 구현입니다.
- 웹앱의 백그라운드에서 메시지 큐의 메시지를 처리하면서
ILogger
과 같은 일반적인 서비스를 공유합니다. - 백그라운드 작업이
Task.Run()
로 시작되었습니다.
기본적으로 이러한 작업을 구현하는 백그라운드 작업으로 오프로드할 수 있습니다 IHostedService
.
하나 이상의 IHostedServices
를 WebHost
또는 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를 구현할 때 관련된 클래스 및 인터페이스의 시각적 요약을 보여 줍니다.
그림 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
부터 시작)에서 백그라운드 작업을 시작하는 편리한 방법을 제공합니다. 주요 이점은 호스트 자체가 종료될 때 백그라운드 작업의 코드를 깔끔하게 정리할 수 있는 우아한 취소 기능을 통해 제공되는 기회입니다.
추가 리소스
ASP.NET Core/Standard 2.0에서 예약된 작업 빌드
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlASP.NET Core 2.0에서 IHostedService 구현
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceASP.NET Core 2.1을 사용하는 GenericHost 샘플
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample
.NET