Go 消息队列实战:用 asynq 基于 Redis 做异步任务、定时与重试
test12026-09-274 次阅读
为什么用 asynq
当邮件发送、图片压缩、报表生成这类耗时操作被塞进 HTTP 请求里,接口就会变慢甚至超时。asynq 是一个基于 Redis 的轻量任务队列,它用 List 存待处理任务、用 ZSet 存延迟与定时任务,提供入队、定时、重试、中间件等开箱即用的能力,适合中小型服务的异步化改造。
定义任务与处理器
任务由类型名和 JSON 负载组成,处理器实现 ProcessTask 接口。这里用泛型结构承载业务参数。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/hibiken/asynq"
)
type EmailPayload struct {
UserID int `json:"user_id"`
Title string `json:"title"`
}
func handleEmailTask(ctx context.Context, t *asynq.Task) error {
var p EmailPayload
if err := json.Unmarshal(t.Payload(), &p); err != nil {
return fmt.Errorf("解析负载失败: %w", err)
}
return sendEmail(p.UserID, p.Title)
}
func sendEmail(userID int, title string) error {
log.Printf("向用户 %d 发送邮件: %s", userID, title)
return nil
}
入队与延迟任务
客户端把任务推入 Redis。用 asynq.ProcessIn 设定相对延迟,用 asynq.MaxRetry 控制失败重试次数,asynq.Queue 指定队列优先级。
func enqueue(client *asynq.Client) error {
payload, _ := json.Marshal(EmailPayload{UserID: 42, Title: "欢迎注册"})
task := asynq.NewTask("email:welcome", payload)
// 10 秒后发送,最多重试 3 次,进入高优队列
_, err := client.Enqueue(
task,
asynq.ProcessIn(10*time.Second),
asynq.MaxRetry(3),
asynq.Queue("critical"),
)
return err
}
启动 Worker 与定时任务
Worker 从 Redis 拉取任务并交给对应处理器。Scheduler 负责周期性任务,例如每天凌晨清理一次日志。
func main() {
redisOpt := asynq.RedisClientOpt{Addr: "127.0.0.1:6379"}
srv := asynq.NewServer(redisOpt, asynq.Config{Concurrency: 10})
mux := asynq.NewServeMux()
mux.HandleFunc("email:welcome", handleEmailTask)
// 定时任务:每天 00:00 执行
scheduler := asynq.NewScheduler(redisOpt, &asynq.SchedulerOpts{})
scheduler.Register("@daily", asynq.NewTask("cleanup:logs", nil))
go func() { log.Fatal(scheduler.Run()) }()
log.Fatal(srv.Run(mux))
}
中间件与错误处理
asynq 的 middleware 可以统一做日志、耗时统计和 panic 恢复,避免每个处理器重复写样板。
func loggingMiddleware(h asynq.Handler) asynq.Handler {
return asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error {
start := time.Now()
err := h.ProcessTask(ctx, t)
log.Printf("任务 %s 耗时 %v 结果 %v", t.Type(), time.Since(start), err)
return err
})
}
// 启动时包裹:srv.Run(asynq.NewServerMiddleware(loggingMiddleware)(mux))
- 任务类型用清晰前缀如 email:welcome,便于在面板里区分业务。
- 失败任务靠 MaxRetry 自动重试,超过上限进入 dead queue 待人工排查。
- 重活放高并发队列,轻活放默认队列,用 Queue 做优先级隔离。
T
test1
文章作者
为什么用 asynq 当邮件发送、图片压缩、报表生成这类耗时操作被塞进 HTTP 请求里,接口就会变慢甚至超时。asy...
- 分类
- 技术
- 发布时间
- 2026-09-27
- 字数
- 约 2210 字
- 阅读
- 4 次
