开发指南:从 `src/jobs` 目录到框架底层调度原理)
Medusa 自定义定时任务Scheduled Jobs开发指南从src/jobs目录到框架底层调度原理【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusaMedusa 的自定义定时任务Scheduled Jobs是运行在应用后台、按指定时间间隔周期执行的函数常用于生成报表、清理数据、同步外部系统等场景。本指南以packages/plugins/loyalty/src/jobs/README.md为核心骨架结合medusajs/framework的JobLoader实现与medusajs/orchestration的调度器源码完整讲解自定义定时任务的创建方式、配置规则、执行机制与进阶用法读完即可在 Medusa 应用中编写并部署自己的定时任务。什么是 Medusa 定时任务定时任务Scheduled Job是 Medusa 后台中按指定时间间隔自动执行的一个函数。你不需要额外引入node-cron之类的第三方库也不需要手动维护一个常驻进程只需在项目中创建一个符合约定的文件Medusa 应用启动时会自动发现、校验并注册它随后由框架内置的调度器负责按时触发。从源码结构看定时任务与订阅者Subscribers是两套平行的扩展机制订阅者监听业务事件如product.created在事件发生时被动执行定时任务基于 cron 时间表达式主动触发与应用内部事件流无关。在packages/plugins/loyalty/src/jobs/README.md中定时任务文件被约定放置在项目的src/jobs目录下。实际加载时medusajs/framework的 JobLoader 会扫描源码目录并将每个合法的任务文件转换为一个带 cron 调度的内部 workflow 注册到工作流引擎中这一点会在后文“底层调度原理”一节详细展开。创建第一个定时任务文件位置与命名约定在 Medusa 项目中创建定时任务需要在项目根目录的src/jobs目录下新建文件文件名没有强约束但建议采用描述性的 kebab-case 或 snake_case 命名如daily-product-report.ts。加载器在扫描资源时会自动排除部分文件。从 resource-loader.ts 的源码可见默认被排除的模式包括以下划线_开头的文件*.spec.ts、*.test.ts等测试文件。也就是说你不必担心单元测试文件被误注册成定时任务。最小可运行示例根据 packages/plugins/loyalty/src/jobs/README.md 的示例创建一个src/jobs/hello-world.tsimport { MedusaContainer } from medusajs/framework/types; export default async function myCustomJob(container: MedusaContainer) { const productService container.resolve(product) const products await productService.listAndCountProducts(); // Do something with the products } export const config { name: daily-product-report, schedule: 0 0 * * *, // Every day at midnight };这个文件导出了两部分内容缺一不可默认导出的处理函数handler任务触发时执行的异步函数config配置对象定义任务的名称与调度规则。文件必须导出什么一个合法的定时任务文件必须导出两个成员导出类型说明default处理函数(container, context?) Promiseunknown每次任务触发时执行的异步函数config配置对象{ name, schedule, numberOfExecutions? }定义任务名称与调度规则config对象的三个属性说明如下name必填任务的唯一名称。它在整个应用中必须唯一因为框架会用它拼接出内部 workflow 名称job-${name}重复会导致注册冲突schedule必填cron 表达式字符串定义任务的执行时间。文档中给出的示例0 0 * * *表示每天午夜执行一次numberOfExecutions可选整数指定任务最多执行多少次后自动移除。例如设为2任务只会在调度器上触发两次之后不再执行。handler 的参数handler 接收一个container参数类型为MedusaContainer它是 Medusa 的依赖注入容器实例。你可以通过container.resolve(...)解析应用内的任何注册资源最常用的是各模块的主服务例如container.resolve(product)—— 商品模块服务其他模块、服务、插件注册的资源同理。值得强调的是这个 handler 实际上还会接收第二个可选参数contextScheduledJobContext。从 types.ts 源码可见其类型定义如下export type ScheduledJobContext { /** * The timestamp for which the job was scheduled. */ scheduledFor: Date }context.scheduledFor表示任务本次计划执行的时间戳。需要“补偿历史任务”或依据计划执行时间做数据筛选的场景可以善用该参数详见下文“进阶使用 scheduledFor”一节。配置详解name任务的唯一标识name用于标识任务也是日志、调试与错误信息中定位任务的关键字。当任务执行失败时JobLoader 会输出类似Scheduled job ${config.name} failed with error: ...的日志此时name直接决定了你能否快速定位到对应文件。schedulecron 表达式schedule是一个标准 cron 表达式。文档示例0 0 * * *的含义是字段取值含义分0第 0 分钟时0第 0 小时午夜日*每天月*每月周*每周的每一天合起来即“每天 00:00 执行”。你可以参考 cron 在线校验工具如 crontab.guru 之类来构造并验证自己的表达式常见的还有*/5 * * * *每 5 分钟执行一次0 9 * * 1每周一上午 9 点执行0 2 * * *每天凌晨 2 点执行。numberOfExecutions限制执行次数numberOfExecutions是一个可选整数属性用于限制任务的总执行次数。设置后任务在调度器上运行指定次数即会被移除不再触发。这非常适合一次性任务、迁移类任务或需要固定次数重试的场景。框架测试夹具中的 order-summary.ts 就是一个同时使用三者的完整示例export default async function handler(container: MedusaContainer) { console.log(You have received 5 orders today) } export const config { name: summarize-orders, schedule: * * * * * *, numberOfExecutions: 2, }这个示例每秒钟触发一次6 段 cron但numberOfExecutions: 2保证了它总共只执行 2 次即被清理非常适合用来验证“限制执行次数”的行为。相关注册逻辑在 register-jobs.spec.ts 中有对应的单元测试覆盖。底层调度原理从文件到定时工作流理解定时任务的底层原理有助于排查调度不生效、并发重复执行等问题。整个链路分三步第一步JobLoader 扫描并校验应用启动时JobLoader 继承自ResourceLoader以src/jobs等目录为源目录递归扫描资源。每发现一个任务文件loadFile 会动态导入它然后执行配置校验validateConfig缺少config抛出Config is required for scheduled jobs.缺少config.schedule抛出Cron schedule definition is required for scheduled jobs.缺少config.name抛出Job name is required for scheduled jobs.。配置不合法时应用会直接报错而不是静默跳过。第二步handler 被包装成 workflow step校验通过后register 方法会做两件事以job-${config.name}命名用createStep把 handler 包装成工作流的一个 step并在 step 内构造ScheduledJobContextscheduledFor取input.scheduledFor或当前时间用createWorkflow将该 step 注册为一个带调度的 workflow。当schedule是字符串时会被归一化为对象形式const workflowConfig { name: workflowName, schedule: isObject(config.schedule) ? config.schedule : { cron: config.schedule, numberOfExecutions: config.numberOfExecutions, }, }也就是说你写的 cron 字符串最终会承载在 workflow 的schedule配置上。第三步WorkflowScheduler 驱动执行调度最终由medusajs/orchestration的 WorkflowScheduler 完成。它通过分布式调度存储IDistributedSchedulerStorage注册 workflowconst normalizedSchedule: SchedulerOptions typeof schedule string ? { cron: schedule, concurrency: forbid, } : { concurrency: forbid, ...schedule, } await WorkflowScheduler.storage.schedule(workflow.id, normalizedSchedule)从 SchedulerOptions 的类型定义可以看到调度配置还支持以下高级选项选项类型默认行为说明cronstring—cron 表达式按时间触发intervalnumber—按毫秒间隔触发与cron二选一concurrencyallow \| forbidforbid是否允许并发执行默认禁止即上一次执行未结束时不会启动下一次numberOfExecutionsnumber无限限制总执行次数值得注意的是concurrency默认是forbid即使你的 cron 表达式非常密集如每 5 秒一次只要前一次 handler 尚未结束调度器就不会重复触发——这避免了长耗时任务自身发生重叠执行。进阶使用 scheduledFor 执行补偿型任务如果你的定时任务因为应用停机、重启等原因错过了一次调度窗口context.scheduledFor可以告诉你“本次应该为哪个时间点补数据”。示例import { MedusaContainer, ScheduledJobContext, } from medusajs/framework/types; export default async function dailyReportJob( container: MedusaContainer, context?: ScheduledJobContext ) { const targetTime context?.scheduledFor ?? new Date() const orderService container.resolve(order) // 以 targetTime 为截止时间查询并生成报表 // const orders await orderService.listOrders({ createdAt: { lte: targetTime } }) } export const config { name: daily-order-report, schedule: 0 0 * * *, };这样即使某个时间段任务没有运行也能根据scheduledFor精确判断应覆盖的数据窗口而不是简单“现在跑一次”。常见问题与排查思路任务从未执行检查schedulecron 表达式是否符合预期必要时用在线 cron 工具验证同时确认文件位于src/jobs目录且文件名不以_开头。应用启动报Config is required for scheduled jobs.文件缺少config导出或config未定义name/schedule可对照 validateConfig 的校验逻辑逐一核对。任务执行失败框架会在日志中输出Scheduled job ${name} failed with error: ...根据name定位任务文件并检查 handler 内抛出的异常。任务意外重复执行确认调度存储如 Redis未配置多个实例重复注册并了解concurrency默认为forbid的防重叠机制。需要临时调试参考框架测试夹具的做法先用schedule: * * * * * *配合numberOfExecutions: 2快速验证任务能被触发再改为生产环境的正式表达式。小结自定义定时任务是 Medusa 扩展体系中非常轻量的一块能力在src/jobs下放一个导出default处理函数与config配置对象的文件即可完成注册。配置对象中的name、schedulecron 表达式与可选的numberOfExecutions控制了任务的标识、触发频率与生命周期container参数让你能够解析任意模块服务完成真实业务context.scheduledFor则为错过窗口的补偿执行提供了依据。底层由 JobLoader 完成校验与 workflow 化由medusajs/orchestration的WorkflowScheduler驱动调度默认禁止并发执行保证任务行为可控可预期。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考