Buffalo 的 buffalo task 命令仅支持一次性同步任务,不提供内置 Worker 或任务队列;真需后台任务,须独立启动进程(如集成 Asynq + Redis),并手动处理上下文取消、序列化参数与 DB 查询。

buffalo task 命令只能跑一次性任务,不是 Worker
Buffalo 没有内置 Worker 进程或后台任务队列。你执行 buffalo task send:email 时,它只是启动一个短生命周期的 Go 进程,同步执行函数,完事就退出——不会常驻、不监听队列、不重试、不并发。这和 Sidekiq、Asynq、Celery 完全不是一回事。
常见错误现象:在 grifts/tasks.go 里写了个耗时函数,然后以为加个 go func() { ... }() 就能后台跑,结果 buffalo task 执行完终端一关,goroutine 被强制终止。
- 所有
buffalo task函数签名必须是func(c buffalo.Context) error,且只用于 CLI 场景 - 它不复用 Web 服务的 DB 连接池,每次都要自己调
models.DB初始化 - 别在函数里起 long-running goroutine:CLI 进程退出后,它们没机会收尾
真要跑 Worker,得自己起独立进程
Buffalo 不反对你加外部任务队列,但它也不会帮你封装启动逻辑。Worker 必须是一个与 Web 服务分离、独立运行的二进制进程(比如 myapp-worker),负责消费消息、执行业务、上报状态。
典型做法是用 github.com/hibiken/asynq + Redis:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 在
main.go或新文件worker/main.go中初始化asynq.NewServer,配置Concurrency和RedisClientOpt - 注册 handler 时不要依赖
buffalo.Context,改用纯函数:如func SendWelcomeEmail(ctx context.Context, t *asynq.Task) error - 任务入队由 Web 层完成:在 action 中调
client.Enqueue(asynq.NewTask("send:welcome", payload)) - 部署时单独起一个容器跑
./myapp-worker,和buffalo dev或./myapp隔离
注意:别把 Worker 启动逻辑塞进 app.go 或 buffalo dev 流程里——热重载会 kill 它,日志混在一起,context 无法统一管理。
参数传递和上下文取消必须手动处理
Buffalo 的 buffalo.Context 在 Worker 进程里根本不存在。你传给任务的数据只能是 JSON 序列化的 map[string]interface{} 或结构体字段,不能带任何 Buffalo 特定类型(比如 pop.Connection 或 render.Engine)。
Worker 函数内部必须主动响应取消信号:
- HTTP 请求要用
http.NewRequestWithContext(ctx, ...),否则 cancel 不生效 - 数据库查询必须用
db.QueryContext(ctx, ...),不能用db.Query() - 大文件读写、循环处理等长操作,需在每次 chunk 后检查
ctx.Err() != nil - 避免
time.Sleep(10 * time.Second),改用select { case
最容易被忽略的是:Worker 进程本身没有优雅退出机制。你得在 main() 里监听 os.Interrupt,调 srv.Shutdown() 等待正在执行的任务完成,而不是直接 os.Exit(0)。

















