🚀 Asynq 学习文档

Asynq 是一个 Go 编写的异步任务队列,基于 Redis 实现,支持任务重试、延迟任务、定时调度等特性。

Asynq 简介

Asynq 是一个简单但强大的异步任务队列系统,常用于:

  • 邮件通知
  • 订单处理
  • 图像/音频转码
  • 后台批量任务
  • 定时调度任务

官网和文档: 🔗 github.com/hibiken/asy...

安装

安装 Redis

bash 复制代码
brew install redis
brew services start redis

或使用 Docker:

bash 复制代码
docker run -d -p 6379:6379 redis

安装 Asynq 库

bash 复制代码
go get github.com/hibiken/asynq

核心概念

概念 说明
Task 一个任务,由类型和载荷组成。
Client 创建任务、加入队列的客户端。
Server 任务执行者(Worker)。
Queue 任务队列,支持多队列
Retry 支持失败自动重试。
Schedule 定时/延迟任务执行。

快速上手

定义任务类型

go 复制代码
// tasks/types.go
const TypeEmailWelcome = "email:welcome"

提交任务(Client)

go 复制代码
client := asynq.NewClient(asynq.RedisClientOpt{Addr: "localhost:6379"})
defer client.Close()
​
payload, _ := json.Marshal(map[string]interface{}{"user_id": 42})
task := asynq.NewTask(TypeEmailWelcome, payload)

info, err := client.Enqueue(task) // 立即执行

Worker 执行任务(Consumer)

go 复制代码
func main(){
  srv := asynq.NewServer(
    asynq.RedisClientOpt{Addr: "localhost:6379"},
    asynq.Config{Concurrency: 10},
  )
​
  mux := asynq.NewServeMux()
  mux.HandleFunc(TypeEmailWelcome,HandleEmailWelcome)
  srv.Run(mux)
}
​
func HandleEmailWelcome(ctx context.Context, t *asynq.Task)error{
  var p map[string]interface{}
  json.Unmarshal(t.Payload(), &p)
  fmt.Println("发送欢迎邮件给用户:", p["user_id"])
  return nil
}
​

任务调度与重试机制

延迟任务

go 复制代码
client.Enqueue(task, asynq.ProcessIn(10*time.Second))

设置过期时间(TTL)

go 复制代码
client.Enqueue(task, asynq.Retention(1*time.Hour))

设置最大重试次数

go 复制代码
client.Enqueue(task, asynq.MaxRetry(3))

Handler 深入探讨

使用 ServeMux

go 复制代码
mux := asynq.NewServeMux()
mux.Handle("email:welcome", welcomeEmailHandler)
mux.Handle("email:reminder", reminderEmailHandler)
mux.Handle("email:", defaultEmailHandler) // catchall for all other task types with a prefix "email:" 

使用 ServeMux,您可以注册多个 Handlers。 它将每个任务的类型与已注册模式的列表进行匹配,并为与任务的类型名称最匹配的模式调用处理程序。

使用 Middleware

如果你需要在任务之前或者任务之后执行一些代码,就可以用middleware来实现。middleware接收一个Handler,并返回 一个Handler的函数。

go 复制代码
func loggingMiddleware(h asynq.Handler) asynq.Handler {
    return asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error {
        start := time.Now()
        log.Printf("Start processing %q", t.Type())
        err := h.ProcessTask(ctx, t)
        if err != nil {
            return err
        }
        log.Printf("Finished processing %q: Elapsed Time = %v", t.Type(), time.Since(start))
        return nil
    })
}

// 使用 middleware
myHandler = loggingMiddleware(myHandler)
// 或
ux := NewServeMux()
mux.Use(loggingMiddleware)

Grouping middlewares

如果想要把某一个中间件用于一组任务,就可以通过组合多个ServeMux来实现。需要注意,每个组中的任务其类型名称中需要有相同的前缀

go 复制代码
productHandlers := asynq.NewServeMux()
productHandlers.Use(productMiddleware) // 应用给product相关任务
productHandlers.HandleFunc("product:update", productUpdateTaskHandler)

orderHandlers := asynq.NewServeMux()
orderHandler.Use(orderMiddleware) // 应用给order相关任务
orderHandlers.HandleFunc("order:refund", orderRefundTaskHandler)

mux := asynq.NewServeMux()
mux.Use(someGlobalMiddleware) // 给所有任务应用
mux.Handle("product:", productHandlers)
mux.Handle("order:", orderHandlers)

if err := srv.Run(mux); err != nil {
    log.Fatal(err)
}

任务的生命周期

将任务放入队列的时候,asynq会在内部对任务进行管理,以确保在指定的时间去使用任务调用处理程序。在次过程中任务就会经历不同的生命周期状态

  • Scheduled :任务正在等待将来处理( 仅适用于具有 ProcessAt 或 ProcessIn 选项的任务 )

ProcessIn: 延迟多久后再执行任务(相对时间)

延迟10秒执行 : client.Enqueue(task, asynq.ProcessIn(10*time.Second)))

ProcessAt: 在某个具体的时间点执行任务(绝对时间)

设定在2小时后执行 :client.Enqueue(task, asynq.ProcessAt(time.Now().Add(2 * time.Hour))))

  • Pending :任务已准备好进行处理,并将由空闲工作程序选取
  • Active : 任务正在由工作程序处理(即 handler 与任务一起调用)。
  • Retry : 无法处理任务,并且任务正在等待将来重试。
  • Archived :任务已达到其最大重试次数,并存储在存档中以供手动检查
  • Completed : 任务已成功处理并保留,直到保留 TTL 过期( 仅适用于具有 Retention 选项的任务 )。

Retention : 用于设置已完成任务在 Redis 中保留多久,之后会自动被删除。

client.Enqueue(task, asynq.Retention(24 * time.Hour))

任务执行成功后,结果在 Redis 中保存 24 小时,然后自动清除

默认情况下,任务执行成功后,Asynq 会立即从 Redis 中删除任务记录

状态转换图

相关推荐
岁忧2 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
Nejosi_念旧2 小时前
解读 Go 中的 constraints包
后端·golang·go
Hellyc2 小时前
用户查询优惠券之缓存击穿
java·redis·缓存
鼠鼠我捏,要死了捏5 小时前
缓存穿透与击穿多方案对比与实践指南
redis·缓存·实践指南
天河归来9 小时前
springboot框架redis开启管道批量写入数据
java·spring boot·redis
守城小轩9 小时前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
漫步向前9 小时前
gin问题知识点汇总
go
mao毛9 小时前
go Mutex 深入理解
go·源码阅读
Charlie__ZS10 小时前
若依框架去掉Redis
java·redis·mybatis
汤姆大聪明11 小时前
Redis 持久化机制
数据库·redis·缓存