🚀 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 中删除任务记录

状态转换图

相关推荐
程序员爱钓鱼16 小时前
Go语言实战案例 — 项目实战篇:简易博客系统(支持评论)
前端·后端·go
TF男孩1 天前
ARQ:一款低成本的消息队列,实现每秒万级吞吐
后端·python·消息队列
AAA修煤气灶刘哥1 天前
别让Redis「歪脖子」!一次搞定数据倾斜与请求倾斜的捉妖记
redis·分布式·后端
郭京京1 天前
go框架gin(中)
后端·go
郭京京1 天前
go框架gin(下)
后端·go
一直_在路上1 天前
Go 语言微服务演进路径:从小型项目到企业级架构
架构·go
往事随风去1 天前
架构师必备思维:从“任务队列”到“事件广播”,彻底吃透消息队列两大设计模式
消息队列·rabbitmq
christine-rr1 天前
linux常用命令(4)——压缩命令
linux·服务器·redis
凯子坚持 c2 天前
精通 Redis list:使用 redis-plus-plus 的现代 C++ 实践深度解析
c++·redis·list
weixin_456904272 天前
跨域(CORS)和缓存中间件(Redis)深度解析
redis·缓存·中间件