NestJS 生命周期

生命周期钩子方法 触发时机 用途 示例场景
onModuleInit() 模块初始化完成时 模块级设置或初始化任务,如建立数据库连接 MongoDB 数据库连接初始化
onApplicationBootstrap() 所有模块初始化后,在侦听连接前 跨模块或全局执行的初始化任务,如启动日志服务 与支付网关的连接建立和外部服务的可用性检查
onModuleDestroy() 收到终止信号后 模块销毁时的清理逻辑,如关闭数据库连接 模块资源清理,如关闭数据库连接
beforeApplicationShutdown() 主动调用 app.close() 或接收到系统终止信号时 应用程序关闭前的整体清理和资源释放 保存状态,关闭外部服务连接,发送关闭通知
onApplicationShutdown() 应用程序即将完全关闭时 应用程序关闭流程的最后阶段执行最终清理操作 资源释放,停止后台进程,清理缓存,记录日志

优雅关闭配置

app.js

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
	// 加入这个
  // Starts listening for shutdown hooks
  app.enableShutdownHooks();

  await app.listen(3000);
}
bootstrap();

@Injectable()
// 实现上述生命周期中的方法,不同生命周期有不同的适用场景
class UsersService implements OnApplicationShutdown {
  onApplicationShutdown(signal: string) {
    console.log(signal); // e.g. "SIGINT"
  }
}

优雅关闭测试

  1. OnModuleDestroy, BeforeApplicationShutdown 用 while true 卡主,没一会就 gc 了; 注意里面要加 sleep
  2. 卡主方式
    1. 准对业务处理可以做本地计数器,如果计数器还有运行中业务就可以卡主;
    2. 也可以做标志,自某个变量之后其他请求直接拒绝,后面自定义补偿;
    3. 对于非严格场景,最好是设置一个最大超时时间
  3. 如果使用 pm2 docker k8s 参考这里
    1. 优雅关闭k8s pod docker pm2 nestjs bull
    2. pm2启动方式配置,pm2最大等待时间配置
    3. pod 下线前的处理
  4. webStorm 的 stop 按钮不好使,自己 build 打包后用 node 运行,ctrl+c 终止,这样测试没有问题(mac 操作系统)

^C
<--- Last few GCs --->

[30626:0x118040000]    52296 ms: Scavenge (reduce) 4037.4 (4109.9) -> 4037.3 (4111.7) MB, 5.0 / 0.0 ms  (average mu = 0.283, current mu = 0.227) allocation failure; 
[30626:0x118040000]    52315 ms: Scavenge (reduce) 4042.3 (4114.9) -> 4042.2 (4116.9) MB, 9.2 / 0.0 ms  (average mu = 0.283, current mu = 0.227) allocation failure; 
[30626:0x118040000]    52327 ms: Scavenge (reduce) 4047.9 (4120.7) -> 4048.0 (4122.2) MB, 5.2 / 0.0 ms  (average mu = 0.283, current mu = 0.227) allocation failure; 

<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x1024a26e4 node::Abort() [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 2: 0x1024a28c8 node::ModifyCodeGenerationFromStrings(v8::Local<v8::Context>, v8::Local<v8::Value>, bool) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 3: 0x1025f960c v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 4: 0x1027a3ec4 v8::internal::EmbedderStackStateScope::EmbedderStackStateScope(v8::internal::Heap*, v8::internal::EmbedderStackStateScope::Origin, cppgc::EmbedderStackState) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 5: 0x1027a7ab0 v8::internal::Heap::CollectSharedGarbage(v8::internal::GarbageCollectionReason) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 6: 0x1027a4ac4 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*, v8::GCCallbackFlags) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 7: 0x1027a1dc4 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 8: 0x1027a0eec v8::internal::Heap::HandleGCRequest() [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
 9: 0x10274e494 v8::internal::StackGuard::HandleInterrupts() [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
10: 0x102b1cfd0 v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
11: 0x102e6d04c Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
12: 0x1080d0d64 
13: 0x102df8198 Builtins_InterpreterEntryTrampoline [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
14: 0x108030350 
15: 0x1080304a4 
16: 0x102e82ffc Builtins_ArrayFrom [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
17: 0x10803b6c4 
18: 0x1080b8fd8 
19: 0x1080b83dc 
20: 0x102df8198 Builtins_InterpreterEntryTrampoline [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
21: 0x102e29ef4 Builtins_AsyncFunctionAwaitResolveClosure [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
22: 0x102eb8738 Builtins_PromiseFulfillReactionJob [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
23: 0x102e1bc4c Builtins_RunMicrotasks [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
24: 0x102df63a4 Builtins_JSRunMicrotasksEntry [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
25: 0x102725d10 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
26: 0x102726200 v8::internal::(anonymous namespace)::InvokeWithTryCatch(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
27: 0x1027263dc v8::internal::Execution::TryRunMicrotasks(v8::internal::Isolate*, v8::internal::MicrotaskQueue*, v8::internal::MaybeHandle<v8::internal::Object>*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
28: 0x10274cb44 v8::internal::MicrotaskQueue::RunMicrotasks(v8::internal::Isolate*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
29: 0x10274d2e0 v8::internal::MicrotaskQueue::PerformCheckpoint(v8::Isolate*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
30: 0x1023e8c4c node::InternalCallbackScope::Close() [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
31: 0x1023e8fd0 node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
32: 0x1023fe36c node::AsyncWrap::MakeCallback(v8::Local<v8::Function>, int, v8::Local<v8::Value>*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
33: 0x102552838 node::(anonymous namespace)::SignalWrap::Start(v8::FunctionCallbackInfo<v8::Value> const&)::'lambda'(uv_signal_s*, int)::__invoke(uv_signal_s*, int) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
34: 0x102ddf608 uv__signal_event [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
35: 0x102de82a8 uv__io_poll [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
36: 0x102dd5e28 uv_run [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
37: 0x1023e96e0 node::SpinEventLoop(node::Environment*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
38: 0x1024df2d4 node::NodeMainInstance::Run() [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
39: 0x10246f338 node::LoadSnapshotDataAndRun(node::SnapshotData const**, node::InitializationResult const*) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
40: 0x10246f5f0 node::Start(int, char**) [/Users/***/.nvm/versions/node/v18.17.0/bin/node]
41: 0x18a90d058 start [/usr/lib/dyld]
[1]    30626 abort      node ./dist/src/main.js