iT邦幫忙

2024 iThome 鐵人賽

DAY 16
2
Software Development

透過 nestjs 框架,讓 nodejs 系統維護度增加系列 第 24

nestjs 系統設計 - 活動訂票管理系統 - graceful shutdown

  • 分享至 

  • xImage
  •  

nestjs 系統設計 - 活動訂票管理系統 - graceful shutdown

目標

在昨天實做完結構化 logger 之後,接著今天將會來實做 graceful shutdown。

概念

所謂的 graceful shutdown 目的在於,當伺服器關閉時需要能有一個妥善釋放資源的設計。比如,當接收到關機的訊號,程式把處理到一半的任務先處理完再關閉,通常會設定一個 Timeout ,限定在這個 Timeout 時間內讓伺服企釋放資源。讓原本的任務有機會寫入其他狀態,等下次重啟再繼續。

在 golang 可以透過 signal channel 來監聽伺服器關閉訊號,然後透過 context 來傳遞訊號到其他 goroutine 通知相關處理資源的 routine 在 timeout 時限內,做完資源釋放。

在 nodejs 這邊主要是透過 process 的事件監聽器來處理這件事情。而在 nestjs 可以透過設定 onModuleDestory 的監測當伺服器關閉的事件,來作邏輯處理。

nestjs 的 Module 可監測的生命週期事件

nestjs 應用程式的生命週期事件,如下圖:

image

其中,綠色的方塊是能夠透過程式去編寫的部份。而要作到 graceful shutdown 。主要讓 app.enableShutdownHooks() 才能偵測到 terminal signal 的訊號。

設定處理非預期的錯誤邏輯

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { Logger, PinoLogger } from 'nestjs-pino';
import { Request } from 'express';
let app: INestApplication;
const pinoLogger = new PinoLogger({
  pinoHttp: {
    autoLogging: false,
    base: null,
    quietReqLogger: true,
    genReqId: (request: Request) => request.headers['x-correlation-id'] || crypto.randomUUID(),
    level: 'info',
    formatters: {
      level (label)  {
        return {level: label }
      }
    },
  }
});
async function bootstrap() {
  app = await NestFactory.create(AppModule, {
    bufferLogs: true,
  });
  app.useLogger(app.get(Logger));
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    transform: true,
  }));
  // enable shutdown event listener
  app.enableShutdownHooks();
  await app.listen(3000);
}
bootstrap();
// setup graceful shutdown logic
const gracefulShutdown = (signal?: unknown) => {
  pinoLogger.info('server gracefulShutdown...', signal);
  app.close().then(() =>{
    pinoLogger.info('server sucessfully shutdown', signal);
    process.exit(0);
  })
  .catch((err: unknown) => pinoLogger.error('server shutdown failed', err));
  setTimeout(() => {
    pinoLogger.error('force to shutdown after 5 seconds');
    process.exit(1);
  }, 5000);
}
process.on('uncaughtException', gracefulShutdown);
process.on('unhandledRejection', gracefulShutdown);

在 RedisService 設定 disconnect

image

驗證 disconnect event

image

結論

適當的 graceful shutdown ,能夠正確的釋放伺服器佔有的資源。避免因為資源被本沒處理好的連線狀態干擾,這對系統狀態極為重要。特別是,如果沒有恰當的去處理,在後面將伺服器作容器化時,就容易看到 exit code 137 的狀態碼,讓資源無法正確的釋放。


上一篇
nestjs 系統設計 - 活動訂票管理系統 - 結構化 logger
下一篇
nestjs 系統設計 - 活動訂票管理系統 containerize
系列文
透過 nestjs 框架,讓 nodejs 系統維護度增加31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言