← 返回博客
go2026-09-12 22:07:582 分钟 · 504 0

Gin 异步任务队列:用 asynq 解耦耗时操作

用 asynq(Redis-backed)把发邮件、生成报表这类耗时操作异步化,请求立即返回,任务在后台 worker 跑,讲清入队、worker、重试和优先级。

#gin#异步任务#asynq

接口里直接发邮件、转码、跑报表,请求要等几秒甚至超时。这些耗时操作和响应用户无关,该丢到后台。这一篇用 asynq 把任务异步化,请求秒回,worker 慢慢处理。

1. 为什么异步

发邮件 200ms、生成报表 5s,都卡在请求链路里,连接和线程被占着,用户干等。这些操作失败也不该让用户请求失败。扔给后台,请求立即返回 202。

2. asynq 基于 Redis

asynq 用 Redis 存任务队列,自带重试、调度、优先级。生产者入队,独立 worker 进程消费:

import "github.com/hibiken/asynq"

client := asynq.NewClient(asynq.RedisClientOpt{Addr: "127.0.0.1:6379"})

func enqueueEmail(to, body string) error {
    payload, _ := json.Marshal(EmailTask{To: to, Body: body})
    t := asynq.NewTask("email", payload)
    return client.Enqueue(t, asynq.MaxRetry(3), asynq.Queue("low"))
}

handler 里调 enqueueEmail 后直接返回,不发完邮件才响应。

3. worker 消费

func main() {
    srv := asynq.NewServer(asynq.RedisClientOpt{Addr: "127.0.0.1:6379"}, asynq.Config{Concurrency: 10})
    srv.Run(asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error {
        switch t.Type() {
        case "email":
            return sendEmail(t.Payload())
        }
        return fmt.Errorf("unknown task: %s", t.Type())
    }))
}

worker 是独立进程,Concurrency 控制并发数。handler 和 API 服务分开部署,互不影响。

4. 失败重试

任务返回 error 就按 MaxRetry 重试,指数退避。重试耗尽进死信队列,别丢:

asynq.NewTask("email", payload, asynq.MaxRetry(5), asynq.Timeout(30*time.Second))

Timeout 防单个任务卡死。死信队列单独监控,堆积说明下游真有问题。

5. 优先级与延迟

client.Enqueue(t, asynq.Queue("critical"))       // 高优队列先消费
client.Enqueue(t, asynq.ProcessIn(24*time.Hour)) // 延迟到明天

报表生成走低优队列,提醒类走高优。延迟任务做定时发券、过期清理。

6. 任务状态可见

asynq 自带 Web UI(asynqmon)看队列长度、失败数、重试中任务。比盲猜强,堆积时能及时加 worker 或查下游。

7. 上线清单

耗时且用户不等结果的操作才异步,强一致写库别异步。worker 独立部署,和 API 资源隔离。任务幂等,重试不会重复发邮件/扣款。失败任务进死信队列并监控,别静默丢。Redis 是任务存处,挂了队列也挂,重要任务考虑持久化或换更稳的 broker。

下一篇讲安全加固,把常见的 Web 攻击挡在门外。

相关推荐

本文为原创文章,采用CC BY-NC-SA 4.0协议授权,转载请保留署名与原文链接。原文链接:https://www.wxbuluo.com/article/197