.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 Generic Host in ASP.NET Core
Use .NET Core Generic Host in ASP.NET Core apps. Generic Host is responsible for app startup and lifetime management.

일반 .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

Worker Console Project 기반으로 Sub-Worker Class Library 의 참조

To-Be -> NET 5

Sub-Worker 가 메인화 (Console Program) 되고, 해당 Worker 프로그램이 Module 만을 참조

변경 지점이 잘 보이는가?  

  • 기존 3.1 때의 Class Library 로 있던 workers 프로젝트들 => Console Program 으로 변경
  • 3.1 때의 Worker 프로젝트는 사라졌다
  • NET5 각 worker 들은 자신의 Program.cs 로 진입해서 IWorker 를 참고한 RunAsync() 구현 클래스의 실행을 하게 된다

각각의 worker는 실행을 하면서 프로젝트에 필요한 참조 DLL 을 갖고, 설정 파일을 각각 설정하고, 디펜던시는 최소화 할 수 있었다 라고 생각했었다

코드는 git repository 참고

NET5 Worker 운영의 문제점

잘 바꿨다고 생각했지만, 역시 다양한 용도의 Worker 들을 사용하다보면 구성쪽 뿐만 아니라 설정 하는 모든 것들에 대해 나뉘어져야 한다고 생각이 들게 되었다.

AppSettings.cs 파일을 Worker Modules 에 넣어놨더니 모든 워커의 설정이 모듈로 모이게 되어, 안쓰는 설정에 대한 부분 까지 다른 worker 에서 보게 되었다. 실제로 게시된 publish outputs 관련 내용은 크게 불편 없었지만, 개발 하다가 겹치는 부분들 및 필요 없는 부분까지 개발 할 때 신경 써야 하는게 마음에 들지는 않았다.
또한 실제 서비스에 올리려고 publish 게시 할 때도 1개의 repository 에서 게시를 하기 때문에 문제가 발생하게 되었다.

다행히 열심히 쓰고 있을 때 쯤 NET6 가 발표소식을 전하면서 2021년 11월 Released 되었다. 이후 NET6 Migration 을 또 한 번 진행하게 된다;;

어떻게든 좋은 방향으로 좋게 변하도록 하는게 목표다 라는 취지로 진행한다

References

Worker Services in .NET
Learn how to implement a custom IHostedService and use existing implementations with .NET.
NSSM - the Non-Sucking Service Manager
GitHub - ssemi/develop-worker-service-with-dotnet: .NET 기술을 활용한 Worker 서비스 개발기
.NET 기술을 활용한 Worker 서비스 개발기. Contribute to ssemi/develop-worker-service-with-dotnet development by creating an account on GitHub.