后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载导读本文是 LoopBack 4 核心教程的第七部分聚焦生命周期事件的观察Observation of life cycle events。LoopBack 4 应用拥有start、stop等运行时生命周期事件而LifeCycleObserver正是让服务、数据源、组件乃至自定义脚本参与这些生命周期、并在启停时执行清理或初始化逻辑的标准机制。本文将以仓库中 greeting-app 示例 的内存缓存清理场景为主线完整讲解如何通过生命周期观察者在应用启动时建立定时清理任务、在停止时释放资源并深入loopback/core的源码说明观察者的发现、分组与编排原理。读完本文你将掌握lifeCycleObserver装饰器、观察者注册、lb4 observer生成命令以及缓存服务与生命周期联动的完整实战方案。生命周期事件与观察者应用启停的旁听者一个 LoopBack 4 应用在运行时拥有自己的生命周期状态通常对应start启动和stop停止两个关键事件。官方生命周期文档 Life-cycle.md 给出了完整的状态机created→booted→started→stopped其中start()会自动调用init()每个应用实例至多一次而stop()负责释放资源并优雅退出。生命周期观察者LifeCycleObserver正是为让各种类型的构件参与应用生命周期而设计服务器在start时开始监听连接、在stop时停止接受新连接数据源在start时建立连接、在stop时断开连接。自定义构件也可以借此在应用启动时执行初始化逻辑、在停止时执行清理逻辑。在 loopback/core 的 lifecycle.ts 中观察者接口被定义为三个可选方法export interface LifeCycleObserver { /** 在 init 阶段调用对同一个应用实例最多调用一次 */ init?(...injectedArgs: unknown[]): ValueOrPromisevoid; /** 在 start 阶段调用 */ start?(...injectedArgs: unknown[]): ValueOrPromisevoid; /** 在 stop 阶段调用 */ stop?(...injectedArgs: unknown[]): ValueOrPromisevoid; }三个方法都是可选的观察者可以只实现自己关心的事件。同时这些方法支持方法级依赖注入Method Injection例如可以直接在start(inject(prefix) prefix: string)中注入绑定值。场景背景greeting-app 的 TTL 内存缓存本文讨论的生命周期观察实战来源于仓库中的 greeting-app 示例构建在loopback/example-greeter-extension之上。该应用对外提供GET /greet/{name}端点返回包含timestamp、language、greeting的问候消息并通过拦截器 内存缓存服务对响应做缓存。缓存服务的 TTL 设计在 caching-service.ts 中CachingService是一个被injectable({scope: BindingScope.SINGLETON})标记的单例服务内部用Mapstring, Message作为存储并为每个缓存条目设置了 time-to-liveTTL生存时间。配置结构如下export interface CachingServiceOptions { // The time-to-live setting for a cache entry ttl: number; }服务的关键操作包括set(key, message)写入缓存条目get(key)读取缓存若条目已过期则返回undefineddelete(key)/clear()删除单个条目或清空缓存isExpired(key, now new Date())判断条目是否过期核心逻辑是now.getTime() - msg.timestamp.getTime() ttl即当前时间减去条目写入时间戳是否超过 TTLsweep()遍历所有缓存键逐个清理过期条目。值得注意的是缓存条目的时间戳来自 types.ts 中定义的Message结构export interface Message { timestamp: Date; language: string; greeting: string; }由于每条响应都携带timestamp缓存服务得以据此计算条目年龄并判断是否过期。缓存由谁写入全局拦截器缓存的实际读写发生在 caching.interceptor.ts 这个全局拦截器中它以Accept-Language语言前缀 请求路径拼出cachingKey先get命中则直接返回缓存结果未命中则调用next()执行业务逻辑并把结果set回缓存const cachingKey ${lang}:${key}; const cachedResult await this.cachingService.get(cachingKey); if (cachedResult) { return cachedResult; } const result await next(); await this.cachingService.set(cachingKey, result); return result;也就是说缓存条目的写入由拦截器完成而过期清理则由生命周期观察者负责——这正是本文的核心主题如何把内存缓存纳入应用生命周期让它随应用启动而开始定期清理、随应用停止而彻底清空。生命周期观察者实战CacheObserver 驱动定时清理缓存条目过期后会被移除这个需求如果只靠get时惰性判断过期条目会一直残留在内存里。greeting-app 的解法是让生命周期观察者在应用启动后周期性执行sweep()主动把已过期的条目清扫出去。观察者实现观察者 cache.observer.ts 的完整实现非常精简import {inject, lifeCycleObserver, LifeCycleObserver} from loopback/core; import {CachingService} from ../caching-service; import {CACHING_SERVICE} from ../keys; /** * This class will be bound to the application as a LifeCycleObserver during * boot */ lifeCycleObserver(caching) export class CacheObserver implements LifeCycleObserver { private timer: NodeJS.Timeout; constructor( inject(CACHING_SERVICE) private cachingService: CachingService, ) {} /** This method will be invoked when the application starts */ async start(): Promisevoid { await this.cachingService.start(); } /** This method will be invoked when the application stops */ async stop(): Promisevoid { await this.cachingService.stop(); } }这里有两个值得注意的细节依赖注入观察者通过inject(CACHING_SERVICE)注入单例缓存服务其中CACHING_SERVICE是在 keys.ts 中定义的强类型绑定键BindingKey.createCachingService(services.CachingService)职责委托观察者本身并不重复实现缓存逻辑而是把start/stop生命周期事件转发给CachingService的同名方法让服务自己管理定时器与清理任务——观察者只是一个生命周期入口。启动时建立周期清扫任务在 CachingService.start() 中应用启动时观察者会调用它逻辑分为三步async start(): Promisevoid { debug(Starting caching service); await this.clear(); const ttl await this.getTTL(); debug(TTL: %d, ttl); this.timer setInterval(() { this.sweep().catch(console.warn); }, ttl); }先清空缓存await this.clear()确保每次启动都从一个干净的内存状态开始避免旧数据残留读取 TTLawait this.getTTL()从配置视图ContextView读取CachingServiceOptions未配置时回退到默认值5000毫秒建立周期任务setInterval(() this.sweep(), ttl)以 TTL 为周期定时清扫过期条目。由于sweep()返回 Promise这里用.catch(console.warn)兜底避免异步异常导致定时器崩溃。sweep()的实现则逐个遍历缓存键并删除过期项async sweep() { debug(Sweeping cache...); for (const key of this.store.keys()) { if (await this.isExpired(key)) { debug(Cache for %s is swept., key); await this.delete(key); } } }停止时释放定时器与内存与start对称CachingService.stop() 负责资源释放async stop(): Promisevoid { debug(Stopping caching service); /* istanbul ignore if */ if (this.timer) { clearInterval(this.timer); } await this.clear(); }这里先用clearInterval(this.timer)取消周期清扫定时器再clear()清空缓存。这样应用停止后不会残留定时器避免 Node.js 进程因挂起定时器而无法退出也不会残留内存数据。此外服务还实现了restart()先stop再start并且在构造函数中监听配置视图的refresh事件当 TTL 等配置在运行期被更新时自动重启服务使新的 TTL 立即生效——这正是config.view()注入ContextView的意义所在。观察者的注册、发现与编排原理看完实例我们再回到框架层理解观察者是如何被应用感知并按序执行的。装饰器与绑定标签lifeCycleObserver(caching)中的caching是观察者分组名。在 lifecycle.ts 中该装饰器本质上是一个injectable语法糖它给绑定打上CoreTags.LIFE_CYCLE_OBSERVER标签通过asLifeCycleObserver并写入CoreTags.LIFE_CYCLE_OBSERVER_GROUP标签记录分组export function lifeCycleObserver(group , ...specs: BindingSpec[]) { return injectable( asLifeCycleObserver, { tags: { [CoreTags.LIFE_CYCLE_OBSERVER_GROUP]: group, }, }, ...specs, ); }在 application.ts 中缓存服务与拦截器通过this.add(createBindingFromClass(...))注册为绑定而观察者则不需要手动注册——它由 Boot 机制按约定自动发现。按约定自动发现LifeCycleObserverBooter在 lifecyle-observer.booter.ts 中内置的LifeCycleObserverBooter标签booter(observers)定义了默认发现规则export const LifeCycleObserverDefaults: ArtifactOptions { dirs: [observers], extensions: [.observer.js], nested: true, };也就是说只要把实现了LifeCycleObserver的类放到src/observers目录、并以*.observer.ts命名app.boot()时就会被load()阶段发现通过isLifeCycleObserverClass校验并自动调用app.lifeCycleObserver(observer)绑定到应用上下文。greeting-app 的目录结构 observers/index.ts 正是这一约定的体现。分组与顺序LifeCycleObserverRegistry当应用start()/stop()时LifeCycleObserverRegistry 会收集所有带LIFE_CYCLE_OBSERVER标签的绑定按分组排序后依次通知分组顺序由orderedGroups配置控制默认[server]例如[datasource, server]表示数据源先于服务器启动停止时按相反顺序执行未列入orderedGroups的自定义分组会排在已列分组之前并按分组名字母序排列同一分组内的观察者默认并行通知parallel: true可配置为false串行执行还支持disabledGroups在运行期禁用某些分组。greeting-app 的CacheObserver属于自定义分组caching因此会先于server分组被启动、后于server分组被停止确保缓存清理任务在 HTTP 服务器开始接收请求前就已就绪。用 CLI 快速生成观察者与其手写观察者类LoopBack 4 提供了官方 CLI 命令 lb4 observerlb4 observer [--group group] [name]name必填的观察者名称可在命令行直接提供否则会进入交互式提示--group group可选的分组名用于控制观察者的执行顺序。交互式提示会依次询问Observer name与Observer group随后 CLI 自动完成两件事创建/src/observers/${observerName}.observer.ts更新/src/observers/index.ts导出新创建的观察者类。生成的模板类已经包含lifeCycleObserver(observer-group-name)装饰器以及init()、start()、stop()三个空方法骨架只需填充自己的逻辑即可详见 Life-cycle-observer-generator.md 中的生成示例。用集成测试验证生命周期联动greeting-app 的集成测试 greeting-service.integration.ts 完整验证了观察者 拦截器 缓存的联动效果it(gets a greeting from cache, async function () { app.configure(CACHING_SERVICE).to({ttl: 100}); // 第一次请求写入缓存 let response await client.get(/greet/Raymond).set(Accept-Language, en).expect(200); const msg1 response.body; // 第二次请求命中缓存结果一致 response await client.get(/greet/Raymond).set(Accept-Language, en).expect(200); expect(response.body).to.eql(msg1); // 等待超过 TTL100ms后缓存过期返回新的时间戳 await promisify(setTimeout)(200); response await client.get(/greet/Raymond).set(Accept-Language, en).expect(200); expect(response.body.timestamp).to.not.eql(msg1.timestamp); });这个用例同时验证了缓存命中时两次响应体完全一致TTL 过期后等待 200ms 100mstimestamp发生变化说明缓存条目已被判为过期并重新生成——这正是isExpired()与生命周期清扫机制共同保证的行为。小结与延伸阅读通过本文可以总结出生命周期观察者的标准使用范式用lifeCycleObserver(分组名)装饰一个实现LifeCycleObserver接口的类把start()/stop()生命周期事件转化为具体的资源管理动作本例中为启动周期清扫定时器、停止时清理缓存将观察者放入src/observers目录交给 Boot 约定自动发现或使用lb4 observer命令一键生成观察者内的业务逻辑尽量委托给注入的服务如CachingService保持观察者薄而清晰利用分组orderedGroups与并行/串行选项控制启停顺序保证依赖关系正确的执行次序。本教程的其余部分继续讲解了相关主题上一节第 6 部分——拦截与观察Interception and observation介绍拦截器的使用下一节第 8 部分——配置Configuration深入讲解 TTL 这类配置的注入与动态更新机制系统性的生命周期文档Life cycle events and observers覆盖应用状态机、优雅关闭shutdown选项与观察者分组的全部细节。赞分享后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载相关推荐LoopBack 4 生命周期观察者生成器用 lb4 observer 快速创建 LifeCycleObserverLoopBack 4 生命周期观察者生成器用 lb4 observer 快速创建 LifeCycleObserver 导读 本文介绍 LoopBack 4 C后端微服务LoopBack 4 示例应用 Greeting-App 深入解析从 REST API 到缓存拦截器与生命周期观察者的完整组装LoopBack 4 示例应用 Greeting App 深入解析从 REST API 到缓存拦截器与生命周期观察者的完整组装 导读 本文以 LoopBack后端微服务LoopBack 4 扩展模块生命周期实现与注册 LifeCycleObserver 的完整指南LoopBack 4 扩展模块生命周期实现与注册 LifeCycleObserver 的完整指南 本文基于 LoopBack 4loopback next后端微服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?