多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Midway 应用生命周期管理:在启动与停止时优雅地初始化和清理资源

Midway 应用生命周期管理:在启动与停止时优雅地初始化和清理资源 后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载Midway 的生命周期Lifecycle机制允许开发者把数据库连接、配置预生成、第三方服务注册等初始化逻辑从请求时提前到应用启动时并在应用停止时优雅释放资源。本文基于src/configuration.ts与ILifeCycle接口完整讲解生命周期钩子的用法、执行顺序与底层实现读完即可在项目启动/停止阶段自如地管理各类资源。为什么需要生命周期钩子在通常情况下我们希望在应用启动的时候做一些初始化或预处理的事情比如创建数据库连接预生成一些配置启动内部定时任务注册第三方 SDK 实例添加全局中间件。而不是把这些工作放到请求响应时才去处理。如果每次请求都重新建立数据库连接、重新解析配置既浪费资源也无法保证服务的稳定与一致性。Midway 提供的生命周期机制正是为此设计的通过src/configuration.ts文件实现ILifeCycle接口就可以在容器 ready 的时候处理这类需求。ready 的准确含义容器 ready而非应用 ready在使用生命周期钩子之前需要先明确一个关键概念这里的 ready 指的是依赖注入容器IoC Containerready并不是应用HTTP server 等ready。因此在容器 ready 阶段你可以对应用做任意扩展比如添加中间件、连接数据库等等。换句话说当onReady等钩子执行时依赖注入容器已经完成装配所有被Provide标记的类与配置对象都已在容器中可用你可以安全地通过Inject获取它们或通过container参数对容器本身做扩展。而 Web 框架如 Koa/Express、服务端口等应用的对外能力是在容器 ready 之后由框架层启动的源码见下文frameworkService.runFramework()的调用时机。最小实现在 src/configuration.ts 中接入生命周期生命周期扩展的入口是src/configuration.ts。用Configuration()装饰一个类并实现ILifeCycle接口即可import { Configuration } from midwayjs/decorator; import { ILifeCycle, IMidwayContainer } from midwayjs/core; Configuration() export class ContainerConfiguration implements ILifeCycle { async onReady(container: IMidwayContainer): Promisevoid { // 容器 ready 时执行 } async onStop(): Promisevoid { // 应用停止时执行 } }从源码看Configuration()装饰器packages/core/src/decorator/common/configuration.ts会自动为该类附加Scope(ScopeEnum.Singleton)与Provide()元数据并以common:configuration作为标识注册到装饰器元数据中。也就是说配置类本身也是一个被容器管理的单例对象天然支持Inject注入其他对象。ILifeCycle 接口的完整定义文档中给出的接口定义如下interface ILifeCycle { /** * 在应用 ready 的时候执行 * param container IoC 容器 * param app 应用 app */ onReady(container: IMidwayContainer, app: IMidwayApplication): Promisevoid; /** * 在应用配置加载后执行 */ onConfigLoad?(container: IMidwayContainer, app: IMidwayApplication): Promisevoid; /** * 在应用停止的时候执行 * param container IoC 容器 * param app 应用 app */ onStop?(container: IMidwayContainer, app: IMidwayApplication): Promisevoid; }对照当前仓库源码 packages/core/src/interface.ts#L559-L585ILifeCycle的实际定义在此基础上还扩展了更多可选钩子export interface ILifeCycle extends PartialIObjectLifeCycle { onConfigLoad?( container: IMidwayContainer, mainApp: IMidwayApplication, options: LifeCycleInvokeOptions ): Promiseany; onReady?( container: IMidwayContainer, mainApp: IMidwayApplication, options: LifeCycleInvokeOptions ): Promisevoid; onServerReady?( container: IMidwayContainer, mainApp: IMidwayApplication, options: LifeCycleInvokeOptions ): Promisevoid; onHealthCheck?( container: IMidwayContainer, mainApp: IMidwayApplication, options: LifeCycleInvokeOptions ): PromiseHealthResult; onStop?( container: IMidwayContainer, mainApp: IMidwayApplication, options: LifeCycleInvokeOptions ): Promisevoid; }各钩子的职责可以归纳为钩子触发时机典型用途onConfigLoad配置加载完成后、onReady之前异步加载/合并配置返回的配置对象会被合并进全局配置onReady依赖注入容器 ready 时连接数据库、注册对象、添加中间件等初始化onServerReady框架/服务启动完成后服务对外可用后的收尾工作onHealthCheck健康检查时自定义健康检查逻辑onStop应用停止时优雅关闭连接、释放资源此外ILifeCycle extends PartialIObjectLifeCycle意味着配置类还可以挂接对象生命周期钩子见 packages/core/src/interface.ts#L630-L636onBeforeBind、onBeforeObjectCreated、onObjectCreated、onObjectInit、onBeforeObjectDestroy用于在容器创建/销毁任意对象时介入处理。这与上述应用级钩子同属生命周期体系但作用对象是 IoC 容器中的每一个对象实例。生命周期钩子的实际执行顺序源码级解析生命周期钩子并非碰巧被调用而是由核心包中的MidwayLifeCycleServicepackages/core/src/service/lifeCycleService.ts按严格顺序驱动。理解这个顺序有助于选择正确的钩子。启动阶段在init()方法中lifeCycleService.ts#L47-L135核心包会通过DecoratorManager.listModule(CONFIGURATION_KEY)收集所有被Configuration()标记的类并从容器中取出其实例支持类写法与函数式写法两种形态执行对象生命周期钩子onBeforeObjectCreated/onObjectCreated/onObjectInit/onBeforeObjectDestroy执行onConfigLoad()——若返回了配置数据会通过configService.addObject(configData)合并进全局配置执行onReady()调用frameworkService.runFramework()真正启动 Web 框架执行onServerReady()。可见文档中强调的ready 是容器 ready与源码完全一致onReady执行时框架尚未启动你可以自由扩展如果需要在服务对外可用之后再做事则应使用onServerReady。停止阶段在stop()方法中lifeCycleService.ts#L137-L149以逆序执行所有配置类的onStop()后启动的先清理符合资源释放的栈式语义调用frameworkService.stopFramework()停止框架。多个配置类的执行顺序当项目中存在多个Configuration()类例如引入了多个组件时源码中runContainerLifeCycle使用isConcurrent: false串行执行每个钩子即按配置类的注册顺序依次执行不会并发。这意味着前一个配置的onReady完成之后才会执行下一个适合存在依赖关系的初始化场景。实战onReady 建立数据库连接、onStop 优雅关闭下面是一个完整的实战示例。我们需要在初始化时提前连接一个数据库。由于配置类本身是容器管理的单例可以通过Inject注入db这样一个数据库连接工具类该实例包含connect和close两个函数import { Configuration } from midwayjs/decorator; import { ILifeCycle, IMidwayContainer } from midwayjs/core; Configuration() export class ContainerConfiguration implements ILifeCycle { Inject() db: any; async onReady(container: IMidwayContainer): Promisevoid { // 建立数据库连接 await this.db.connect(); } async onStop(): Promisevoid { // 关闭数据库连接 await this.db.close(); } }这样我们就能在应用启动时建立数据库连接而不是在请求响应时再去创建同时在应用停止时也能优雅地关闭数据库连接避免连接泄漏或数据库侧的残留会话。需要注意钩子方法应返回Promise并使用await等待异步资源就绪源码中runLifeCycle会await cycle.instancelifecycle等待钩子完成后再进入下一步onReady阶段拿到的container是全局应用容器applicationContextonStop同样会收到容器与应用实例参数可按需使用如果初始化逻辑中依赖其他被容器管理的对象应通过Inject注入而非自行new以保证实例的唯一性与生命周期一致性。实战onConfigLoad 异步加载配置如果你的某些配置来自远程、文件或数据库需要在启动阶段异步获取并合并进全局配置可以实现onConfigLoad。从源码可见该钩子的返回值会被configService.addObject(configData)合并到全局配置对象中后续所有地方都可以通过常规的配置注入方式读取import { Configuration } from midwayjs/decorator; import { ILifeCycle, IMidwayContainer } from midwayjs/core; Configuration() export class ContainerConfiguration implements ILifeCycle { async onConfigLoad(container: IMidwayContainer): Promiseany { // 异步加载配置并返回返回值会被合并进全局配置 const remoteConfig await fetchRemoteConfig(); return { remote: remoteConfig, }; } }仓库中的测试夹具 packages/core/test/fixtures/base-app-config-async-load/src/configuration.ts 正是对这一能力的使用验证可以作为参考。实战registerObject 扩充注入对象生命周期钩子还有一个重要用途对默认注入的对象做扩充。在onReady中可以通过container.registerObject(name, instance)将任意对象尤其是无法用装饰器改造的三方包注册进容器随后在其他类中直接Inject使用。例如注册一个 sequelize 三方包对象import { Configuration } from midwayjs/decorator; import * as sequelize from sequelize; Configuration() export class ContainerConfiguration implements ILifeCycle { async onReady(container: IMidwayContainer): Promisevoid { // 三方包对象 container.registerObject(sequelize, sequelize); } }在其他的类中可以直接注入使用export class IndexHandler { Inject() sequelize; async handler() { console.log(this.sequelize); } }从源码看registerObject是容器接口的一部分容器对象 packages/core/src/context/container.ts#L362-L363 将其委托给注册表实现 packages/core/src/context/definitionRegistry.ts#L131最终把该对象作为一个可直接按名字获取的依赖项纳入 IoC 体系。这也是组件包向应用暴露实例如Inject()一个连接池对象的底层机制之一。钩子超时与资源释放配置从MidwayLifeCycleService源码中可以确认各生命周期钩子均支持超时控制对应的配置项位于core命名空间下配置项作用core.configLoadTimeoutonConfigLoad的超时时间core.readyTimeoutonReady的超时时间core.serverReadyTimeoutonServerReady的超时时间core.stopTimeoutonStop的超时时间这些超时通过createPromiseTimeoutInvokeChain实现避免某个配置类的初始化或清理逻辑卡死导致应用无法启动/停止。具体数值可以在应用的配置文件如src/config/config.default.ts中按需调整。小结与进一步阅读Midway 的生命周期机制把启动时初始化、停止时清理沉淀为框架级能力通过src/configuration.tsILifeCycle即可在容器 ready 阶段连接数据库、注册三方对象、异步加载配置并在停止时优雅释放资源。其执行顺序onConfigLoad→onReady→ 框架启动 →onServerReady停止时逆序执行onStop由核心包的MidwayLifeCycleService统一驱动保证确定性。如果希望继续深入推荐阅读同一版本文档中的依赖注入与容器container.md了解Inject、registerObject背后的 IoC 容器机制服务与依赖注入service.md掌握对象作用域与生命周期之间的关系装饰器 APIdecorator_api.md查看Configuration等装饰器的完整参数说明。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Fx 应用生命周期全解析初始化、启动钩子与优雅关停Fx 应用生命周期全解析初始化、启动钩子与优雅关停 导读 本文深入剖析 Uber FxGo 的依赖注入应用框架中 fx.App 的完整生命周期从 fx.后端开发工具FastAPI 生命周期管理实战用 Lifespan 事件优雅实现启动初始化与关闭清理FastAPI 生命周期管理实战用 Lifespan 事件优雅实现启动初始化与关闭清理 在 FastAPI 中你可以定义一类只在 应用启动前执行一次、或应用后端Web框架API设计FastAPI 生命周期事件Lifespan完整指南在应用启动前初始化、退出后清理共享资源FastAPI 生命周期事件Lifespan完整指南在应用启动前初始化、退出后清理共享资源 在真实生产应用中总会有一类代码需要在应用 开始接收任何请求之后端Web框架API设计上一篇Elixir项目中关于异常处理的常见误区解析下一篇5分钟快速上手Bongo Cat Mver让你的键盘操作变成可爱动画创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表