- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
在本篇技术指南中,我们将围绕 Redwood 的execCLI 命令,完整演示如何借助 Faktory 这一语言无关的持久化后台任务服务器,构建一个可以脱离请求/响应周期运行的异步 Worker:从生成脚本、注册任务、配置环境变量,到在 Service 中投递任务、最终启动 Worker 消费任务,全流程可复制可运行。读完本文,你将掌握在 Redwood 应用中落地"注册后发欢迎邮件"这类后台任务的完整实现方案,并理解yarn rw exec的底层执行机制。
为什么需要后台任务与 Faktory
在典型的 Web 应用中,一些操作(如发送欢迎邮件、同步第三方数据、清理过期数据)并不适合阻塞在 GraphQL Mutation 的请求/响应周期里。它们耗时长、依赖外部服务,一旦失败还会拖垮用户请求。这类工作最适合放到后台异步执行。
Faktory 是一个语言无关(language-agnostic)、持久化的后台任务服务器:它本身与语言无关,任何语言的客户端都可以与它通信;任务数据被持久化保存,即使 Worker 进程崩溃,任务也不会丢失。在 Redwood 应用中,我们通过faktory-worker这个 Node 客户端库与 Faktory 服务器通信——Worker 端用它注册并消费任务,应用端(Service)用它把任务投递到服务器。
整体架构分为三部分:
- Faktory 服务器:负责接收、排队并持久化任务(可用 Docker 一键启动);
- Redwood Worker 脚本:由
yarn rw g script生成的 Node 脚本,注册任务处理器,长驻运行等待消费任务; - Redwood 应用:在 Service(如注册逻辑)中把任务推送到 Faktory 服务器。
第一步:生成 Worker 脚本
Redwood 的generate script命令会在./scripts/<name>下生成一个可独立执行的 Node.js 脚本文件。打开终端执行:
yarn rw g script faktoryWorker生成器会输出类似如下的提示,告诉我们如何调用这个脚本:
✔ Generating script file... ✔ Successfully wrote file `./scripts/faktoryWorker.ts` ✔ Next steps... After modifying your script, you can invoke it like: yarn rw exec faktoryWorker yarn rw exec faktoryWorker --param1 true从生成器模板(见 script.ts.template)可以看出,脚本默认结构如下,脚本默认导出函数接收{ args },其中args._是位置参数数组,其余为具名参数:
// Append api/* to import from api and web/* to import from web // To access your database uncomment the line below // import { db } from 'api/src/lib/db' interface Args { _: string[] [key: string]: unknown } export default async ({ args }: Args) => { // Your script here... console.log(':: Executing script with args ::') console.log(args) }关键点在于:脚本中可以使用api/src/...前缀导入 API 侧的服务与库(如$api/src/lib/logger、api/src/lib/db),这正是 Worker 能复用 Redwood 业务代码的基础。如果你的项目启用了 TypeScript,生成器会自动生成.ts文件(参见 generate script 文档)。
第二步:注册任务处理器
编辑scripts/faktoryWorker.js,用faktory-worker注册一个名为postSignupTask的任务,并在默认导出函数中启动 Worker 长驻进程:
const { postSignupTask } from '$api/src/lib/tasks' import { logger } from '$api/src/lib/logger' import faktory from 'faktory-worker' faktory.register('postSignupTask', async (taskArgs) => { logger.info("running postSignupTask in background worker") await postSignupTask(taskArgs) }) export default async ({ _args }) => { const worker = await faktory .work({ url: process.env.FAKTORY_URL, }) .catch((error) => { logger.error(`worker failed to start: ${error}`) process.exit(1) }) worker.on('fail', ({ _job, error }) => { logger.error(`worker failed to start: ${error}`) }) }逐段解读这段代码:
faktory.register('postSignupTask', ...):向 Worker 注册任务名称及其处理函数。Faktory 服务器推送过来名为postSignupTask的任务时,会执行这里注册的异步回调;faktory.work({ url: process.env.FAKTORY_URL }):让 Worker 连接 Faktory 服务器并开始消费任务,连接地址来自环境变量FAKTORY_URL;连接失败时记录错误并process.exit(1)退出;worker.on('fail', ...):监听单个任务执行失败事件,便于集中记录失败原因(示例代码中的日志文案与启动失败相同,实际项目中可改为"任务执行失败"等更贴切的描述)。
目前这段代码还不能运行,因为postSignupTask还没有在api/src/lib/tasks.js中定义,FAKTORY_URL也还没有配置。
第三步:配置 FAKTORY_URL
在项目根目录的.env文件中设置FAKTORY_URL,指向你的 Faktory 服务器地址:
FAKTORY_URL=tcp://localhost:7419Faktory 默认监听 7419 端口(Web UI 默认在 7420)。如果使用 Docker 启动服务器,请确认端口映射与本配置一致。只有FAKTORY_URL配置正确、服务器在线,Worker 才能启动并消费任务。
第四步:实现后台任务
在api/src/lib/tasks.js中实现postSignupTask。这个函数负责真正执行后台工作,例如调用外部邮件服务发送欢迎邮件——这类耗时、依赖外部网络的操作正适合放到后台:
export const postSignupTask = async ({ userId, emailPayload }) => { // Send a welcome email to new user. // You'll have to have an integration with an email service for this to work. await sendEmailWithTemplate({ ...emailPayload, TemplateModel: { ...emailPayload.TemplateModel, }, }) }这里sendEmailWithTemplate是对邮件服务商的封装(实际项目需自行实现或接入第三方服务)。函数接收{ userId, emailPayload }作为任务参数,这两个字段正是后续从 Service 投递任务时传入的数据。由于tasks.js位于api/src/lib,Worker 脚本通过$api/src/lib/tasks导入即可复用同一份实现,业务逻辑只写一次。
第五步:在 Service 中投递任务
任务定义好后,需要在合适的业务节点把它投递出去。对postSignupTask来说,最合理的位置是用户完成注册之后——通常是会被 GraphQL Mutation 调用的 Service。下面是一个src/services/auth/auth.js的示例:
const faktory = require('faktory-worker') export const signUp = async ({ input }) => { // Perform all the signup operations, such as creating an entry in the DB and auth provider // ... // Then, send our task to the Faktory server const client = await faktory.connect() await client.job('postSignupTask', { ...taskArgs }).push() await client.close() }这段代码的关键动作:
faktory.connect():建立到 Faktory 服务器的客户端连接(地址同样取自FAKTORY_URL);client.job('postSignupTask', { ...taskArgs }).push():将postSignupTask任务连同参数推送到服务器。Faktory 会持久化该任务并等待 Worker 消费;client.close():投递完成后关闭连接,释放资源。
注意这里的任务参数(taskArgs)会作为taskArgs传给 Worker 中的处理函数,因此在postSignupTask实现中解构的{ userId, emailPayload }必须与这里投递的数据结构保持一致。
至此,一条完整的后台任务链路已经打通:用户注册 → Service 投递任务到 Faktory → Worker 消费任务 → 执行postSignupTask发送欢迎邮件。
启动服务器并运行 Worker
先用 Docker 启动 Faktory 服务器(需要提前安装 Docker 并拉取 Faktory 镜像,映射好 7419 端口),然后在项目根目录运行 Worker:
yarn rw exec faktoryWorker如果 Faktory 服务器在线、FAKTORY_URL配置正确,你会看到服务器拾取 Service 投递的任务,Worker 进程随即消费并处理这些任务。
exec命令本身提供了一些实用选项(定义见 exec.js):
| 选项 | 别名 | 默认值 | 说明 |
|---|---|---|---|
name(位置参数) | — | — | 要运行的脚本文件名(扩展名可省略) |
--prisma | — | true | 运行脚本前是否生成 Prisma Client |
--list | -l | false | 仅列出所有可用脚本,不执行 |
--silent | -s | false | 静默 Redwood 自身的输出,只保留脚本输出 |
例如查看当前项目有哪些脚本可以运行:
yarn rw exec --listexec 底层是如何工作的
理解exec的执行机制,有助于排查脚本运行问题。核心实现在 execHandler.js,其处理流程大致如下:
- 定位脚本:通过
findScripts扫描scripts/目录下的所有脚本文件,并按文件名去扩展名分组;未指定脚本名或使用--list时,会列出所有可用脚本; - 解析参数:CLI 通过 yargs 解析命令,
exec [name]会把脚本名放入name变量;其余位置参数进入args._(并剔除exec与$0),具名参数如--firstParam 'hello'则作为属性传入,最终组合成传给脚本的args对象; - 注册 Babel 钩子:脚本中
$api/src/...、api/src/...这类别名导入之所以可用,是因为执行器注册了 API 侧的 Babel 转换钩子(registerApiSideBabelHook),让脚本在运行时能解析 Redwood 的路径别名与 ESM 语法; - 生成 Prisma Client:默认在运行前生成 Prisma Client(可通过
--prisma false关闭),保证脚本中使用db时数据库访问可用; - 执行脚本:调用脚本默认导出的异步函数,传入解析好的
args。
官方文档对exec的定位是:执行yarn redwood generate script <name>生成的脚本,用于一次性操作、长任务或工具脚本(见 exec 章节),典型场景包括:将 Stripe 产品同步到数据库的一次性脚本、可离线处理长任务的后台 Worker、开发期的自定义种子数据脚本等。
更进一步的参考
- 如果你希望深入了解
generate script的参数选项(如--typescript、--rollback),参见 CLI 命令参考; - 运行
exec时如果想观察脚本收到的参数,可在脚本内console.log(args),执行yarn rw exec faktoryWorker foo --firstParam 'hello'即可看到{ _: ['foo'], firstParam: 'hello' }形式的输出; - 若项目需要更框架化、内建的后台任务能力(与 Prisma 深度集成、自带重试与队列管理),可进一步阅读 background-jobs.md,将其与本文的"Exec + Faktory"轻量方案对比选型。
小结
本文完整演示了在 Redwood 中使用execCLI 命令与 Faktory 搭建后台 Worker 的五个步骤:生成脚本、注册任务、配置FAKTORY_URL、实现任务函数、在 Service 中投递任务,并最终通过yarn rw exec faktoryWorker启动消费。这套方案把耗时操作从请求/响应周期中剥离,同时借助 Faktory 的持久化能力保证任务不丢失,是 Redwood 应用处理发送邮件、同步第三方数据等异步工作的实用选择。
- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
相关推荐
Redwood 实战:基于 Exec 命令与 Faktory 构建后台任务 Worker
Redwood 实战:基于 Exec 命令与 Faktory 构建后台任务 Worker 本文是一份面向 Redwood 应用开发者的实战指南,核心主题是借助
后端前端Web框架开发工具Redwood 后台任务实战:使用 exec 命令与 Faktory 构建后台 Worker
Redwood 后台任务实战:使用 exec 命令与 Faktory 构建后台 Worker 导读 本文基于 Redwood 官方 How To 文档( doc
后端前端Web框架开发工具Redwood 后台任务实战:用 exec 命令与 Faktory 构建后台 Worker(version 4.x)
Redwood 后台任务实战:用 exec 命令与 Faktory 构建后台 Worker(version 4.x) 本文是一份面向 Redwood 应用开发者
后端前端Web框架开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考