
后端微服务云原生【免费下载链接】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 3.x 的midwayjs/cron组件为主线系统讲解如何在标准项目与一体化项目中快速搭建每台机器、每个进程都会执行的本地定时任务从组件安装、Job任务类编写、onTick/onComplete钩子到任务对象的注入获取、手动启停、请求作用域上下文、组件日志与全局配置并深入组件源码packages/cron印证底层实现。读完你可以独立完成一个可运行、可管理、可观测的本地任务模块并清楚它与你可能用到的 Bull 分布式任务队列之间的边界。本地任务 vs 分布式任务队列midwayjs/cron与midwayjs/bull都是 Midway 提供的任务调度方案但二者定位完全不同cron 组件提供的是本地任务能力即在每台机器的每个进程都会执行任务。它不依赖 Redis任务调度信息保存在进程内存中适合单机、单进程场景下的周期任务如数据清理、定时同步、状态巡检。bull 组件是分布式任务队列基于 Redis 存储任务数据可以在不同机器、不同进程之间只执行一次任务。如果你需要整个集群只执行一次的语义请使用 bull 组件。从组件源码看cron 组件在package.jsonpackages/cron/package.json中仅依赖cron: 3.5.0即底层调度由 npm 生态的cron包完成组件在其上封装了 Midway 风格的装饰器、IoC 容器与框架生命周期能力。相关信息描述可用于标准项目✅可用于 Serverless❌可用于一体化✅包含独立主框架✅包含独立日志✅注意cron 任务运行在当前 Node.js 进程内因此不支持 Serverless 场景标准项目与一体化项目均可使用。安装组件在项目根目录执行$ npm i midwayjs/cron3 --save或者在package.json中增加如下依赖后重新安装{ dependencies: { midwayjs/cron: ^3.0.0 // ... } }使用组件注册到配置模块将组件加入Configuration的imports中import { Configuration } from midwayjs/core; import * as cron from midwayjs/cron; Configuration({ imports: [ // ... cron ] }) export class MainConfiguration { // ... }在 packages/cron/src/configuration.ts 中可以看到组件以namespace: cron注册并在启动时自动注入CronFramework、注册InjectJob属性装饰器的处理器registerPropertyHandler同时在onReady阶段调用framework.loadConfig()加载全局配置。也就是说只要引入组件任务类的扫描、注册与框架能力会全部自动就绪。编写任务处理类Job 与 IJob使用Job装饰器装饰一个类即可快速定义一个任务处理器。比如在src/job目录中创建sync.job.ts用于数据同步任务// src/job/sync.job.ts import { Job, IJob } from midwayjs/cron; import { FORMAT } from midwayjs/core; Job({ cronTime: FORMAT.CRONTAB.EVERY_PER_30_MINUTE, start: true, }) export class DataSyncCheckerJob implements IJob { async onTick() { // ... } }要点Job用于修饰任务类框架初始化时会自动将其转变为一个任务见下文源码分析。任务类必须实现IJob接口定义于 packages/cron/src/interface.ts并实现onTick方法。每当任务触发时框架会自动调用onTick。onTick是核心执行入口返回结果会传递给可选的onComplete。可选的 onComplete 钩子IJob接口中onComplete是可选方法onComplete?(result: any)在onTick完成后执行可用于收尾记录Job({ cronTime: FORMAT.CRONTAB.EVERY_PER_30_MINUTE, start: true, }) export class DataSyncCheckerJob implements IJob { async onTick() { // ... } async onComplete() { // 记录一些数据等等用处不是很大 } }从 packages/cron/src/framework.ts 的addJob实现可以确认完整调用链框架把onTick包装进匿名请求上下文执行先经过守卫runGuard与中间件再调用service.onTick.bind(service)随后await service.onComplete?.(result)把onTick的返回值传给onComplete若onTick抛出异常会被捕获并写入ctx.logger.error(err)不影响下一次调度。Job 装饰器常用参数参数类型描述cronTimestringcrontab 表达式startboolean是否自动启动任务runOnInitboolean是否在初始化就执行一次其中cronTime对应底层cron包v3.5.0的 crontab 语法支持秒级精度格式为* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun) │ │ │ │ └───── month (1 - 12) │ │ │ └────────── day of month (1 - 31) │ │ └─────────────── hour (0 - 23) │ └──────────────────── minute (0 - 59) └───────────────────────── second (0 - 59, optional)Midway 内置常用 Cron 表达式Midway 在midwayjs/core中预置了常用表达式常量定义于 packages/core/src/util/format.tsimport { FORMAT } from midwayjs/core; // 每隔 30 分钟执行的 cron 表达式 FORMAT.CRONTAB.EVERY_PER_30_MINUTE表达式对应时间CRONTAB.EVERY_SECOND每秒钟CRONTAB.EVERY_MINUTE每分钟CRONTAB.EVERY_HOUR每小时整点CRONTAB.EVERY_DAY每天 0 点CRONTAB.EVERY_DAY_ZERO_FIFTEEN每天 0 点 15 分CRONTAB.EVERY_DAY_ONE_FIFTEEN每天 1 点 15 分CRONTAB.EVERY_PER_5_SECOND每隔 5 秒CRONTAB.EVERY_PER_10_SECOND每隔 10 秒CRONTAB.EVERY_PER_30_SECOND每隔 30 秒CRONTAB.EVERY_PER_5_MINUTE每隔 5 分钟CRONTAB.EVERY_PER_10_MINUTE每隔 10 分钟CRONTAB.EVERY_PER_30_MINUTE每隔 30 分钟也可以直接书写 crontab 字符串例如*/2 * * * * *表示每隔 2 秒执行一次。装饰器源码Job 做了什么查看 packages/cron/src/decorator.ts 的Job实现可以看清其三个作用通过DecoratorManager.saveModule(CRON_JOB_KEY, target)把任务类登记为模块CRON_JOB_KEY cron:job见 packages/cron/src/constants.ts供框架在启动时统一扫描通过MetadataManager.defineMetadata保存jobOptions与jobName元数据自动为任务类附加Provide()与Scope(ScopeEnum.Request)即每个任务类是一个请求作用域的 IoC 对象每次触发都会从请求上下文容器中获取实例执行。任务管理命名任务与手动启停除了让任务按cronTime自动运行还可以通过框架 API 对任务进行手动管理。下面的代码仅仅定义了一个任务不会自动启动执行Job(syncJob, { cronTime: */2 * * * * *, // 每隔 2s 执行 }) export class DataSyncCheckerJob implements IJob { async onTick() { // ... } }这里通过Job的第一个字符串参数给任务命名为syncJob并设置了默认调度时间。Job装饰器支持两种签名Job(jobOptions?)与Job(jobName, jobOptions?)源码见 packages/cron/src/decorator.ts。获取任务对象任务对象类型为CronJob来自底层cron包。有两种获取方式方式一InjectJob装饰器注入参数为任务类本身或任务名字符串// src/configuration.ts import { Configuration, Inject } from midwayjs/core; import * as cron from midwayjs/cron; import { InjectJob, CronJob } from midwayjs/cron; import { DataSyncCheckerJob } from ./job/sync.job; Configuration({ imports: [ cron ], }) export class ContainerConfiguration { InjectJob(DataSyncCheckerJob) syncJob: CronJob; InjectJob(syncJob) syncJob2: CronJob; async onServerReady() { // this.syncJob this.syncJob2 } }其底层由 packages/cron/src/configuration.ts 的init方法实现registerPropertyHandler(CRON_JOB_KEY, ...)会把属性值解析为this.framework.getJob(meta.jobName)因此按类注入和按名称注入最终拿到的是同一个CronJob实例。方式二Framework API 获取通过注入的cron.Framework调用getJob同样支持传类或名字// src/configuration.ts import { Configuration, Inject } from midwayjs/core; import * as cron from midwayjs/cron; import { InjectJob, CronJob } from midwayjs/cron; import { DataSyncCheckerJob } from ./job/sync.job; Configuration({ imports: [ cron ], }) export class ContainerConfiguration { Inject() cronFramework: cron.Framework; async onServerReady() { const syncJob this.cronFramework.getJob(DataSyncCheckerJob); const syncJob2 this.cronFramework.getJob(syncJob); // syncJob syncJob2 } }:::caution注意任务对象必须在onServerReady生命周期或者应用启动之后才能获取。因为任务注册发生在框架run()阶段早于该生命周期无法取到实例。:::关于命名从 packages/cron/src/framework.ts 的getJobName可以看到传入类时优先读取元数据中的jobName未命名时回退为DecoratorManager.getProviderUUId(name)生成唯一标识传入字符串则直接作为任务名。启停任务CronJob对象提供start()/stop()方法。可以在初始化或某个流程完成后手动启动// src/configuration.ts import { Configuration, Inject } from midwayjs/core; import * as cron from midwayjs/cron; import { InjectJob, CronJob } from midwayjs/cron; import { DataSyncCheckerJob } from ./job/sync.job; Configuration({ imports: [ cron ], }) export class ContainerConfiguration { InjectJob(DataSyncCheckerJob) syncJob: CronJob; async onServerReady() { this.syncJob.start(); // ... this.syncJob.stop(); } }这也是测试夹具中的常见用法查看 packages/cron/test/fixtures/base-app/src/configuration.ts其在onServerReady中调用this.framework.getJob(HelloTask).start()启动任务并断言注入对象与 Framework 获取对象严格相等assert.ok(this.helloTask this.framework.getJob(HelloTask))。任务执行上下文与请求作用域任务执行发生在请求作用域中有着特殊的 Context 对象结构定义于 packages/cron/src/interface.tsexport interface Context extends IMidwayContext { job: CronJob; }这里的CronJob类型来自底层cron包。从 packages/cron/src/framework.ts 的addJob实现可以看到每次任务触发时框架会执行self.app.createAnonymousContext({ job: this, from: name })创建匿名的请求上下文并把当前CronJob挂到ctx.job上通过MidwayTraceService.runWithEntrySpan为本次执行创建入口 Spanspan 名形如cron ${name.name}携带midway.protocol: cron、midway.cron.job等属性支持通过组件配置tracing控制开关、扩展 extractor 与 meta因此 cron 任务可以无缝接入 Midway 的链路追踪体系packages/cron/test/index.test.ts 中有对应测试验证runWithEntrySpan被调用执行守卫runGuard(ctx, name, onTick)与请求上下文中间件然后从ctx.requestContext.getAsyncIJob(name)获取任务实例并调用onTick。因此在任务类中可以直接Inject()注入ctx: Context并通过ctx.job操作当前任务实例测试夹具 packages/cron/test/fixtures/base-app/src/task/hello.task.ts 中就有this.ctx.job.stop()的用法。组件日志cron 组件拥有独立日志默认会将ctx.logger记录在midway-cron.log文件中。可以单独配置这个 logger 对象export default { midwayLogger: { // ... clients: { // ... cronLogger: { fileLogName: midway-cron.log, }, } } }组件默认的 logger 名称为cronLogger见 packages/cron/src/framework.ts 中的protected frameworkLoggerName cronLogger。组件在 packages/cron/src/configuration.ts 中自带了默认日志配置并且预设了一个上下文格式化函数contextFormat: info { const { from } info.ctx; return ${info.timestamp} ${info.LEVEL} ${info.pid} [${from.name}] ${info.message}; }即日志行会携带触发该次执行的from.name任务类名方便按任务维度检索日志覆盖cronLogger配置时可按需替换该格式。全局配置defaultCronJobOptions可以针对 Job 进行全局配置这些配置会与每个 Job 的配置进行合并export default { cron: { defaultCronJobOptions: { // ... } } }defaultCronJobOptions对应底层cron包的CronJobParamsonTick/onComplete除外具体类型CronJobOptions见 packages/cron/src/interface.ts。其合并逻辑位于 packages/cron/src/framework.ts 的addJob中jobOptions extend( true, {}, this.defaultCronJobConfig, options.jobOptions, jobOptions );即通过深合并deep extend依次叠加全局默认配置 → 装饰器上的任务配置 →addJob调用时传入的运行时配置后者覆盖前者。例如可以在全局配置中统一设置所有任务的默认timeZone、是否自动start等公共参数单个任务再通过Job参数做局部覆盖。框架生命周期中的任务管理结合源码梳理 cron 组件的完整生命周期组件初始化packages/cron/src/configuration.ts注册InjectJob属性装饰器处理器onReady时调用loadConfig()读取cron.defaultCronJobOptions。框架启动packages/cron/src/framework.ts 的run()通过DecoratorManager.listModule(CRON_JOB_KEY)扫描所有Job装饰的类逐一调用addJob创建CronJob并存入Mapstring, CronJob。运行期任务按 crontab 触发onTick在请求作用域中执行可通过getJob获取、start/stop启停。框架停止beforeStop()会遍历所有任务名并调用deleteJob(name)逐个stop()并移出 Map保证进程退出前任务被干净地关闭。测试验证与可参考的示例组件自带测试packages/cron/test/index.test.ts覆盖了核心行为自动执行定义start: true的任务应用启动 5 秒后断言任务已执行app.getAttr(task) 1异常隔离onTick抛出错误时不影响任务继续调度夹具见 packages/cron/test/fixtures/base-app-err/src/task/hello.task.ts该测试模拟throw new Error(test error)后应用仍正常运行按类名/字符串获取任务getJob(DataSyncCheckerJob)与getJob(syncJob)均能取到同一个任务链路追踪集成任务每次执行都会创建入口 Span。这些测试夹具packages/cron/test/fixtures/base-app本身就是一份最小可运行的示例HelloTask展示了带命名、cronTime、start/runOnInit注释的任务类写法以及通过ctx.job.stop()在任务内部停止自己的用法。小结midwayjs/cron为 Midway 应用提供了轻量、免依赖无需 Redis的本地定时任务能力用Job声明任务、实现IJob.onTick/onComplete完成业务逻辑通过InjectJob或cron.Framework.getJob获取并启停任务再配合独立的midway-cron.log日志与cron.defaultCronJobOptions全局配置即可投入使用。需要强调其边界本地任务在每个进程都会执行跨机器/跨进程只执行一次的分布式场景请转向 bull 组件。赞分享后端微服务云原生【免费下载链接】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 本地任务Cron组件指南Job 定时任务、任务管理与框架 APIMidway 本地任务Cron组件指南Job 定时任务、任务管理与框架 API 导读 midwayjs/cron 是 Midway 框架内置的本地任务后端微服务云原生MidwayJS cron 组件本地定时任务实战从 Job 装饰器到任务管理与全局配置MidwayJS cron 组件本地定时任务实战从 Job 装饰器到任务管理与全局配置 定时任务是后端应用几乎必备的基础能力Midway 为此提供了 m后端微服务云原生Midway 任务调度实战分布式定时任务、延迟任务与本地 Cronmidwayjs/taskMidway 任务调度实战分布式定时任务、延迟任务与本地 Cronmidwayjs/task 导读 本文聚焦 Midway 框架中基于 midwayj后端微服务云原生上一篇SuperCom终极多串口调试工具5大功能助你轻松搞定嵌入式开发与工业自动化测试下一篇TV Bro电视浏览器让智能电视上网变得如此简单创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考