news 2026/9/14 14:25:33

Wasp 后台任务(Jobs)实战指南:基于 PgBoss 的延迟、重试与定时任务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Wasp 后台任务(Jobs)实战指南:基于 PgBoss 的延迟、重试与定时任务

Wasp 后台任务(Jobs)实战指南:基于 PgBoss 的延迟、重试与定时任务

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

本文以 Wasp 官方文档 Recurring Jobs 为核心,系统讲解 Wasp 后台任务(Jobs)的完整使用方式:如何在main.wasp中声明任务、如何编写 worker 函数、如何在 Operation 与 setupFn 中提交任务,以及如何配置定时调度、延迟执行、失败重试与任务跟踪。读完后你将能够在自己的 Wasp 应用中把耗时的业务逻辑(发邮件、调用外部 API、异步数据处理)安全地挪到后台执行,并掌握PgBoss执行器的底层原理、数据库结构与常见坑位。

为什么需要后台任务

在大多数 Web 应用中,用户向服务器发请求、服务器返回数据,请求处理得越快,应用给人的感觉就越流畅。但有些请求服务器需要额外的时间才能完整处理,例如:

  • 发送一封邮件;
  • 调用外部 API 的慢速 HTTP 请求;
  • 处理需要秒级甚至分钟级耗时的数据。

此时更好的做法是:先尽快给用户返回响应,把剩余工作放到后台去做。Wasp 通过后台任务(Jobs)支持这种模式,并且开箱即用地提供以下能力:

  • 任务在服务器重启后依然保留(持久化);
  • 任务失败后可以自动重试;
  • 任务可以被延迟到未来的某个时间点执行;
  • 任务可以按 cron 表达式定时重复执行。

快速上手:声明并运行你的第一个 Job

下面以官方文档中的经典示例展开:一个打印消息到控制台、并从数据库返回任务列表的 Job。

第 1 步:在main.wasp中声明 Job

在 Wasp 应用的main.wasp文件中添加job声明:

job mySpecialJob { executor: PgBoss, perform: { fn: import { foo } from "@src/workers/bar" }, entities: [Task], }

这里的三个核心字段:

  • executor: PgBoss—— 指定任务执行器,目前 Wasp 唯一支持PgBoss
  • perform.fn—— 指向实现任务逻辑的 worker 函数(从@src/下的 NodeJS 文件导入);
  • entities: [Task]—— 声明该任务内部需要使用哪些实体,Wasp 会把它们注入到 worker 函数的context.entities中。

第 2 步:实现 worker 函数

src/workers/bar.js中实现foo

export const foo = async ({ name }, context) => { console.log(`Hello ${name}!`) const tasks = await context.entities.Task.findMany({}) return { tasks } }

TypeScript 版本(Wasp 会为每个 Job 生成同名、首字母大写的泛型类型,例如MySpecialJob):

import { type MySpecialJob } from 'wasp/server/jobs' import { type Task } from 'wasp/entities' type Input = { name: string; } type Output = { tasks: Task[]; } export const foo: MySpecialJob<Input, Output> = async ({ name }, context) => { console.log(`Hello ${name}!`) const tasks = await context.entities.Task.findMany({}) return { tasks } }

worker 函数规范(官方文档明确要求):

  • 必须是async函数;
  • 函数的返回值就是该 Job 的执行结果;
  • 函数接收两个参数:
    • args:提交任务时传入的数据;
    • context: { entities }:包含你在 Job 声明中列出的实体的上下文对象。

类型安全说明:MySpecialJob是 Wasp 生成的泛型类型,用于正确标注 worker 函数的入参和返回值类型。它接收两个类型参数——Inputargs的类型)和Output(返回值的类型)。详见下文 JavaScript API 一节。

第 3 步:在 Operation / setupFn 中提交任务

Job 定义完成后,就可以在你的 Operations(Query/Action)或 setupFn(以及其他任意 NodeJS 代码)中提交任务:

import { mySpecialJob } from 'wasp/server/jobs' const submittedJob = await mySpecialJob.submit({ name: "Johnny" }) // 或者延迟到未来执行:.delay() 接受秒数、Date 或 ISO 日期字符串 await mySpecialJob .delay(10) .submit({ name: "Johnny" })

做完以上三步,你的 Job 就会被PgBoss执行,效果等同于直接调用foo({ name: "Johnny" })。注意:传给 Job 的参数不是强制要求的,是否传参完全取决于你如何实现 worker 函数。

定时任务(Recurring Jobs)

如果你有需要定期重复执行的工作,可以在 Job 声明中添加schedule

job mySpecialJob { executor: PgBoss, perform: { fn: import { foo } from "@src/workers/bar" }, schedule: { cron: "0 * * * *", args: {=json { "job": "args" } json=} // 可选 } }

配置了schedule之后,你不需要在 JavaScript/TypeScript 中主动调用任何提交方法——可以想象成foo({ job: "args" })每小时被自动调度并执行一次。

API 参考:Job 声明的完整字段

一个完整的 Job 声明如下:

job mySpecialJob { executor: PgBoss, perform: { fn: import { foo } from "@src/workers/bar", executorOptions: { pgBoss: {=json { "retryLimit": 1 } json=} } }, schedule: { cron: "*/5 * * * *", args: {=json { "foo": "bar" } json=}, executorOptions: { pgBoss: {=json { "retryLimit": 0 } json=} } }, entities: [Task], }

executor: JobExecutor(必填)

任务执行器负责任务的调度、监控与执行PgBoss是目前唯一的执行器,适用于低流量的生产场景,且要求你的app.db.systemPostgreSQL

Wasp 之所以选择 pg-boss 作为第一个任务执行器,是因为它借助 PostgreSQL(及其SKIP LOCKED机制)作为存储与同步手段,能在不引入额外基础设施和复杂管理的情况下,提供大部分任务队列该有的能力。

perform: dict(必填)

  • fn: ExtImport(必填):执行具体工作的async函数。由于 Wasp 在服务器端执行 Job,导入路径必须指向 NodeJS 文件。它接收两个参数:args(提交时传入的数据)和context: { entities }(包含已声明实体的上下文对象)。
  • executorOptions: dict:提交任务时使用的执行器默认选项,会被直接透传给执行器。可在调用时用submit()schedule中的配置覆盖。其中pgBoss: JSON对应 pg-boss 的 send 选项(如retryLimit)。

schedule: dict

  • cron: string(必填):5 位占位符格式的 cron 表达式字符串。pg-boss 的调度只精确到分钟级,这是其设计如此的原因。需要帮助时可以借助在线 cron 表达式校验工具来生成和检查表达式。
  • args: JSON:定时触发时传给perform.fn的参数。
  • executorOptions: dict:定时提交时使用的执行器选项。perform.executorOptions是默认值,schedule.executorOptions可以覆盖/扩展它;其中pgBoss: JSON同样对应 pg-boss 的 send 选项。

entities: [Entity]

在 Job 内部需要使用到的实体列表,用法与 Queries/Actions 中的实体声明 一致。

深入 PgBoss:工作原理与数据库结构

数据存储在独立的pgbossschema 中

当你把 pg-boss 加入 Wasp 项目后,它会自动在数据库中新增一个名为pgboss的 schema,内含一些内部跟踪表,包括jobschedule。pg-boss 的大多数表都有一个name列,对应你在.wasp文件中定义的 Job 标识符。此外,这些表还维护参数、状态、返回值、重试信息、开始与过期时间,以及其他 pg-boss 所需的元数据。

注意:使用PgBoss时,数据库的初始化由 Wasp 服务器自动完成,不需要在 schema 或迁移文件中手动建表。

与服务器同进程运行

Wasp 在启动 web 服务器应用的同时会启动 pg-boss,两者同时在线。这意味着:

  • 通过 pg-boss 运行的任务与服务器其他逻辑(如 Operations)共享 CPU,因此应避免在 Job 中运行 CPU 密集型任务;
  • Wasp 目前不支持对纯 pg-boss 应用做独立的水平扩展,也不支持将其作为独立 worker/进程/线程启动。

从源码看,pg-boss 的生命周期管理位于 pgBoss.ts 模板:startPgBoss()通过boss.start()准备目标 PostgreSQL 数据库并开始任务监控,若数据库对象不存在会自动创建;且保证整个服务器生命周期内只启动一次。

通过PG_BOSS_NEW_OPTIONS定制 pg-boss 实例

如果需要定制 pg-boss 实例的创建参数,可以设置环境变量PG_BOSS_NEW_OPTIONS,其值为一个包含 pg-boss 初始化参数的字符串化 JSON 对象。

注意:设置该变量会覆盖 Wasp 的所有默认值,因此必须同时包含数据库连接信息。从模板源码可见,Wasp 的默认配置是{ connectionString: config.databaseUrl }(即直接复用 Wasp 的数据库连接串),仅当PG_BOSS_NEW_OPTIONS存在时才会用JSON.parse解析并整体替换默认配置。

例如,要设置连接串并调整任务的归档与删除策略:

# 在 .env 文件中 PG_BOSS_NEW_OPTIONS={"connectionString":"postgresql://user:password@server:5432/database","archiveCompletedAfterSeconds":86400,"deleteAfterDays":30,"maintenanceIntervalMinutes":5} # 在 shell 中 PG_BOSS_NEW_OPTIONS='{"connectionString":"postgresql://user:password@server:5432/database","archiveCompletedAfterSeconds":86400,"deleteAfterDays":30,"maintenanceIntervalMinutes":5}'

如果要在 Heroku 上部署,还需要额外设置:

PG_BOSS_NEW_OPTIONS={"connectionString":"<REGULAR_HEROKU_DATABASE_URL>","ssl":{"rejectUnauthorized":false}}

这是因为 pg-boss 使用的pg扩展默认不会通过 SSL 连接,而 Heroku 要求 SSL,且其证书是自签名的,必须显式关闭证书校验。

任务数据的保留与清理

默认情况下,PgBoss在任务完成或失败后保留数据 12 小时,之后移入归档表,归档数据再保留 7 天后删除。若要改变这一行为,可通过PG_BOSS_NEW_OPTIONS配置归档(archiveCompletedAfterSeconds/archiveFailedAfterSeconds)与删除(deleteAfterSeconds/deleteAfterDays等)参数,例如上面的示例将已完成任务归档时间设为 86400 秒(1 天)、删除时间设为 30 天。

已知问题:重命名带调度的 Job

Wasp 从 Job 声明中推导任务名,并在 pg-boss 各表的name列中使用该名称。如果你重命名了一个原本带有schedule的 Job,pg-boss 仍会继续按旧名字调度,但此时已没有对应的 handler,任务会变成 stale 并最终过期。解决方法是删除pgbossschema 中schedule表里对应的行;如果只是从 Job 声明中移除schedule,也需要做同样处理。

JavaScript API:提交、延迟与跟踪

导入 Job

import { mySpecialJob } from 'wasp/server/jobs'

TypeScript 中可同时导入生成的泛型类型:

import { mySpecialJob, type MySpecialJob } from 'wasp/server/jobs'

MySpecialJob这类泛型类型以 Job 声明命名为基础生成(例如声明名为mySpecialJob,则类型名为MySpecialJob),位于wasp/server/jobs模块,用于类型安全的 worker 函数标注,接收InputOutput两个类型参数。

submit(jobArgs, executorOptions)

向执行器提交一个 Job,可选的jobArgs是 worker 函数接收的 JSON 参数,可选的executorOptions是执行器相关的提交选项:

const submittedJob = await mySpecialJob.submit({ job: "args" })

delay(startAfter)

延迟 worker 函数的调用时间,startAfter可以是:

  • 整数:延迟的秒数(默认 0);
  • 字符串:ISO 日期字符串,表示在该时间点执行;
  • Date:在该时间点执行。
const submittedJob = await mySpecialJob .delay(10) .submit({ job: "args" }, { "retryLimit": 2 })

任务跟踪(Tracking)

submit()返回一个SubmittedJob实例,包含以下字段:

  • jobId:任务在执行器中的 ID;
  • jobName:你在.wasp文件中使用的任务名;
  • executorName:任务执行器名称的 Symbol。

此外还有执行器命名空间下的专用对象。对于 pg-boss,可以通过pgBoss访问:

  • details():获取 pg-boss 特有的任务详细信息;
  • cancel():尝试取消一个任务;
  • resume():尝试恢复一个已取消的任务。

源码视角:Wasp 如何解析与生成 Jobs

声明解析:Wasp.AppSpec.Job

Wasp 编译器(Haskell 实现)在 AppSpec/Job.hs 中定义了 Job 声明的数据结构,与.wasp语法一一对应:

  • Job:由executorperformschedule(可选)、entities(可选)组成;
  • JobExecutor:目前只有PgBoss一个构造器,JSON 解析时只接受字符串"PgBoss",其他值直接报错;
  • Perform:包含fn(外部导入的函数)与可选的executorOptions
  • Schedule:包含cron(注释中明确标注为 5 字段 cron 表达式,如"*/5 * * * *")、可选的argsexecutorOptions
  • ExecutorOptions:目前只有pgBoss一个 JSON 字段,会被直接透传给执行器。

校验规则:PgBoss 必须搭配 PostgreSQL

在 AppSpec/Valid.hs 的validateAppSpec校验链中,有一项专门的validateDbIsPostgresIfPgBossUsed校验——只要项目里声明了 Job(使用了 PgBoss),数据库系统就必须是 PostgreSQL,否则编译器会直接报错。这与文档中"要求app.db.systemPostgreSQL"的说明完全对应。

代码生成:server/jobs模块

Wasp 的生成器 JobGenerator.hs 负责为每个 Job 生成 SDK 代码:

  • 生成wasp/server/jobs模块的index.ts,导出所有 Job 及其类型;
  • 为每个 Job 生成独立的.ts文件(_job.ts模板),其中typeName由 Job 名首字母大写得到(例如mySpecialJobMySpecialJob),并把实体的 Prisma 数据、schedule(cron、args、options)与perform选项编译进生成的代码;
  • 仅在项目存在任意 Job 时才生成core/job.ts与执行器相关文件(core/pgBoss/)。

这意味着你写的.wasp声明是"唯一事实来源",类型安全与执行细节全部由编译期生成保证。

实战参考:Kitchen Sink 示例中的完整用法

仓库中的 Kitchen Sink 示例是学习 Jobs 的最佳参考,相关文件位于 examples/kitchen-sink/src/features/jobs/。

Job 声明(jobs.wasp.ts)

jobs.wasp.ts 演示了三种典型声明:

job(uppercaseTextJob, { executor: "PgBoss", entities: ["UppercaseTextRequest"], }), job(mySpecialJob, { executor: "PgBoss", performExecutorOptions: { pgBoss: { retryLimit: 1 } }, entities: ["Task"], }), job(mySpecialScheduledJob, { executor: "PgBoss", schedule: { cron: "0 * * * *", args: { foo: "bar" }, executorOptions: { pgBoss: { retryLimit: 2 } }, }, }),

可以看到:第一个是普通任务;第二个通过performExecutorOptions设置了失败重试 1 次;第三个配置了每小时执行一次的定时调度,并单独为调度设置了重试 2 次。

在 Action 中提交任务

uppercaseText.ts 演示了"Action 立即响应、Job 异步处理"的完整模式:ActionrequestUppercaseText先把请求写入数据库(状态PENDING),然后jobs.uppercaseTextJob.submit({ requestId: request.id })提交后台任务,立即返回requestId给前端;worker 函数uppercaseTextJob随后在后台读取请求、模拟 2 秒处理、把文本转大写后写回数据库(状态SUCCESS),出错时捕获异常并更新状态为ERROR后重新抛出。这是一个非常适合移植到你自己应用中的"异步任务 + 状态机"模板。

Worker 函数

bar.js 展示了 worker 函数的另一种写法:mySpecialJobmySpecialScheduledJob复用一个内部runBarJob异步函数,打印参数与上下文、模拟 4 秒耗时并返回{ hello: "world" }

小结

Wasp 的 Jobs 体系把"后台任务"从繁琐的队列基础设施中抽象出来:你只需在.wasp中做一次声明,就能获得持久化、重试、延迟、定时调度与类型安全,底层由 PostgreSQL 之上的 PgBoss 提供可靠的存储与同步。使用时请记住三条关键约束:数据库必须是 PostgreSQL;任务与服务器共享进程与 CPU,不要放 CPU 密集型工作;重命名带调度的 Job 后需要手动清理pgboss.schedule表。在此基础上,Jobs 完全可以承担 Web 应用常见的低流量后台队列场景(邮件、外部 API 调用、异步数据转换等)。

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:24:00

表情识别毕设实战:从CNN模型训练到实时摄像头部署

简介&#xff1a;面向计算机专业学生&#xff0c;这是一份基于Pytorch构建的卷积神经网络面部表情识别毕业设计项目&#xff0c;评审得分高达98分&#xff0c;且已严格调试可运行。资源包内含2000个文件&#xff0c;其中有1992张JPG表情图像、6个Python源码、1个CSV标注文件及1…

作者头像 李华
网站建设 2026/9/14 14:23:55

SAR点目标仿真全链路:回波生成、距离徙动校正与指标验证

简介&#xff1a;合成孔径雷达&#xff08;SAR&#xff09;点目标仿真与RD成像算法是雷达信号处理教学中的经典内容。这份MATLAB工程资料面向正在学习SAR成像原理、需要完成点目标仿真实验的本科生、研究生及相关领域工程师&#xff0c;能够帮助读者在真实代码层面理解距离压缩…

作者头像 李华
网站建设 2026/9/14 14:21:46

uni-app社区团购APP三端落地实战:蓝牙核销与多端适配

简介&#xff1a;这是一份基于uni-app开发的社区团购类APP源码模板&#xff0c;面向前端开发者与跨端应用学习者&#xff0c;聚焦社区生鲜电商场景&#xff0c;提供开箱即用的购物流程、拼团机制与多端适配能力。资源包为ZIP格式&#xff0c;大小935KB&#xff0c;虽未提供具体…

作者头像 李华
网站建设 2026/9/14 14:20:27

2026年企业自动化运维产品选型对比:四类主流架构的差异与决策逻辑

概述 2026年&#xff0c;企业IT运维正从“脚本自动化”向“平台智能化”结构性跃迁。IDC数据显示&#xff0c;2024年中国IT智能运维软件市场规模已达34.1亿元&#xff0c;一体化运维平台市场未来三年复合增长率接近10%&#xff1b;全球企业智能运维解决方案市场预计从2025年的6…

作者头像 李华