프레임워크/NestJS

NestJS Graceful shutdown

트리맨스 2023. 8. 13. 00:48
반응형

 

실행중인 NestJS를 종료하고 싶은데, 실행중인 작업이 있으면 종료하기 애매할 것이다. 해당 세션은 종료가 된 이후에 업데이트가 되어야 하기 때문이다. 이러한 문제를 NestJS는 shutdown에 관련한 인터페이스를 제공하여 SIGTERM 신호가 들어올 때의 동작을 정의한다.

 

구현


import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ShutdownSignal } from '@nestjs/common';

const PORT = 3001;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableShutdownHooks([ShutdownSignal.SIGTERM]);
  await app.listen(PORT);

  console.log(`Application is running on: ${PORT}`);
  console.log(`Pid is ${process.pid}`);
}
bootstrap();

enableShutdownHooks 메서드를 호출한다. 이 기능은 기본으로는 비활성화 되어있는데, 이는 공식 문서에서는 메모리를 많이 소비하므로 필요할 때만 키는 것 같다. 

 

이제 service를 보자. 기본적으로 implement할 수 있는 인터페이스를 제공하므로, 해당 메서드를 사용하여 구현해 보았다.

import {
  Injectable,
  OnModuleDestroy,
  OnApplicationShutdown,
  BeforeApplicationShutdown,
} from '@nestjs/common';

@Injectable()
export class AppService
  implements OnModuleDestroy, BeforeApplicationShutdown, OnApplicationShutdown
{
  /**
   * 5초 대기하는 함수
   */
  getHello(): Promise<string> {
    console.log('5초 대기');
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log('5초 대기 완료');
        resolve('Hello World! ');
      }, 5000);
    });
  }

  async onModuleDestroy() {
    console.warn('모듈이 종료됩니다');
  }

  async beforeApplicationShutdown(signal: string) {
    console.warn(`프로그램이 종료되기 전에 ${signal} 시그널을 받았습니다`);
  }

  async onApplicationShutdown() {
    console.warn('프로그램이 종료됩니다');
  }
}

 

해당 메서드를 구현한 이후 IDLE 상태의 애플리케이션에  SIGTERM 신호를 주게되면, 애플리케이션이 onModuleDestory -> beforeAplicationShutdown-> onApplicationShutdown 순서대로 실행되는 것을 볼 수 있다.

 

여기서 5초가 걸리는 getHello 로직(세션)이 실행 중일때 SIGTERM을 주게 되면, 애플리케이션이 바로 종료되지 않는다. 여기서 몇가지 실험을 해 봤는데, getHello 세션 실행 중에 SIGTERM을 주면은 어플리케이션이 종료가 되지는 않는다. 하지만 getHello 세션이 종료가 되어도 프로그램이 종료되지 않는 것을 보았다. 똑같은 요청을 날리면 여전히 실행되는 것을 보았다. 그러다가 몇초 뒤에 갑자기 애플리케이션이 종료가 되었다.

 

실무에서 사용할때는 보통 SIGTERM이 들어간 이후 몇 분 뒤에 SIGKILL이 들어가서 애플리케이션을 완전히 죽여버린다. SIGTERM은 애플리케이션이 종료되기 전에 준비 과정(?)을 할 시간을 준다. 하지만 SIGKILL은 준비 과정을 거치지 않고 강제로 죽여버린다. 그래서 애플리케이션을 종료할 때는 SIGTERM을 먼저 보내서 애플리케이션에서 종료 준비를 한 후에 SIGKILL로 완전하게 프로세스를 죽인다고 한다.

 

반응형