.NET 기술을 활용한 Worker 서비스 개발기 -4-
지난 번에 .NET Core 3.1 로 Worker Service 를 구현 하면서 생긴 이슈를 가지고 NET5 로 Migration 하면서 구조도 많이 변경 하게 되었다
또한 글을 다 쓰고 github 에 코드도 올리고 확인하니, 서비스화 시키는 내용이 빠진 것을 확인했다. 보통은 dockerize 해서 사용 하기 때문에, docker run 프로세스를 계속 실행하게 두는 형태로 사용하지만, worker 의 경우에는 Windows OS에서 서비스에 올려서 사용 하기 위해서
NSSM - the Non-Sucking Service Manager
이라고 하는 가벼운(?) 프로그램을 하나 사용한다. 꽤나 강력한 프로그램이고, 입맛대로 설정을 할 수 있게 되어있어서 굉장히 좋다.
윈도우 서비스에 등록할 때, 기본 서비스로 등록하지 말고, 위 프로그램을 사용해서 등록하면 조금 더 다양한 옵션을 선택할 수 있을 것이다.
내가 많이 사용하는 것은 CPU Processor 실행 갯수 제한 같은 옵션이 있다
.NET5 로의 Migration 과정
글을 쓰는 과정은 다들 알고 있듯이 NET6 발표 이후다
이 글의 본래 목적도 NET을 사용하여 Worker 프로젝트가 어떻게 변화 하는 과정을 설명하는게 목적이다.
일단 .NET5 가 발표되었고, 사용도 해보려고 기존 Worker 구성을 변경 하기로 했다.
구성원들과 GIT 저장소를 공통으로 사용하면서 쓰다보니 단점이 확실히 되었던 여러 문제들을 해결 하려고 생각해봤다.
- 설정 파일들의 내용 추가 시 포함되는 불편함
- 모든 worker 프로젝트를 참조하는 문제로 인한
- 불필요한/사용하지 않는 다른 Worker DLL 포함 등등
그러다가 기존 구성을 역전(Inversion) 시키면 참조 문제도 줄어들고, 모든 구성 문제도 해결이 될 수 있다는 생각이 들어서 .NET5 로 작업할 때는 구성을 변경해서 작업 진행하기로 했다.
강력한 Generic Host 기능
기능적으로는 이미 NET Core 3.1에서 알고 있었기에, 테스트 삼아서 사용해보니 어마어마한 느낌이라 다음에 프로젝트에 적용하려고 마음먹었다.
우선 Generic Host 에 대한 설명은 자세한건 MSDN 가서 보는게 좋다
일반 .NET Console 프로그램에서 프로그램을 실행하는데 Generic Host 를 사용하게 되면, DI를 비롯한 많이 사용하는 서비스를 쉽게 컨트롤이 가능해진다. 실제로 application 의 수명주기를 관리하게 되는데, 이로서 시작과 끝을 컨트롤이 가능해진다.
또한 Host 기능을 사용해서 Program 을 구성하면 전체적으로 동일한 구성을 사용하도록 만들 수 있고, (=동일한 Host 코드 및 서비스 디펜던시 설정에 대한 부분을 공통화) Console 프로그램에서 DI를 강제 할 수 있기 때문에, Worker 들을 구성하기 훨씬 간편해진다.
변경이 되어가는 과정
기존에 Worker 폴더의 Program.cs 진입점을 각 Workers 프로젝트로 이전 시키려고 하다보니 공수가 조금 있길래, 그냥 새로 만드는게 빠르겠다 싶어서 이름을 강제로 변경하고 다시 만들었다. 사실 프로젝트 OutputType만 하나 추가 하면 되겠다 보이긴 했지만, 그냥 깔끔하게 만들어지는걸 더 선호할 뿐이다.
Program.cs 파일을 보면
internal class Program
{
static void Main(string[] args)
{
// Generic Host Setup
// DefaultBuilder 이후 체이닝 형태로 AppConfiguration 및 Service 관리가 가능하다
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config.SetWorker(env); // 환경에 따른 공통 구성 설정
})
.ConfigureServices((hostContext, services) =>
{
var env = hostContext.HostingEnvironment;
var config = hostContext.Configuration;
services.SetWorker(env, config); // 공통 서비스 설정
})
.Build();
var svc = ActivatorUtilities.CreateInstance<Runner>(host.Services);
svc.DoAction(string.Empty); // Runner Class 의 DoAction 를 실행한다
}
}
으로 구현해서 Runner.cs
파일에서 구현한 코드를 그대로 DoAction 메소드 실행 하는 내용이다.
DoAction 에서는 실제 큐 소비 (Consume) 에 대한 로직이 담겨 있고, 각 worker 들의 RunAsync() 구현을 실행 하도록 되어있다.
NET Core 3.1 에서 NET5 로 구성된 프로젝트의 변경점은 다음과 같다.
As-Is NET Core 3.1
To-Be -> NET 5
변경 지점이 잘 보이는가?
- 기존 3.1 때의 Class Library 로 있던 workers 프로젝트들 => Console Program 으로 변경
- 3.1 때의 Worker 프로젝트는 사라졌다
- NET5 각 worker 들은 자신의 Program.cs 로 진입해서
IWorker
를 참고한 RunAsync() 구현 클래스의 실행을 하게 된다
각각의 worker는 실행을 하면서 프로젝트에 필요한 참조 DLL 을 갖고, 설정 파일을 각각 설정하고, 디펜던시는 최소화 할 수 있었다 라고 생각했었다
NET5 Worker 운영의 문제점
잘 바꿨다고 생각했지만, 역시 다양한 용도의 Worker 들을 사용하다보면 구성쪽 뿐만 아니라 설정 하는 모든 것들에 대해 나뉘어져야 한다고 생각이 들게 되었다.
AppSettings.cs
파일을 Worker Modules 에 넣어놨더니 모든 워커의 설정이 모듈로 모이게 되어, 안쓰는 설정에 대한 부분 까지 다른 worker 에서 보게 되었다. 실제로 게시된 publish outputs 관련 내용은 크게 불편 없었지만, 개발 하다가 겹치는 부분들 및 필요 없는 부분까지 개발 할 때 신경 써야 하는게 마음에 들지는 않았다.
또한 실제 서비스에 올리려고 publish 게시 할 때도 1개의 repository 에서 게시를 하기 때문에 문제가 발생하게 되었다.
다행히 열심히 쓰고 있을 때 쯤 NET6 가 발표소식을 전하면서 2021년 11월 Released 되었다. 이후 NET6 Migration 을 또 한 번 진행하게 된다;;
어떻게든 좋은 방향으로 좋게 변하도록 하는게 목표다 라는 취지로 진행한다