Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Asynq Go Redis Docker 实战:任务队列、优先级与多 Worker

A production-oriented Asynq example for Go: Docker Redis, client and worker implementation, weighted and strict priority queues, retries, delayed jobs, Asynqmon dashboard, and multi-replica Kubernetes guidance.

这是一个从零搭建的 Go 异步任务队列示例,适合学习和验证 Asynq、Redis、Docker Compose、后台任务、延迟任务、失败重试、队列优先级、多 Worker 副本与 Asynqmon 可视化监控。

这是一个可直接运行的 Asynq 实验项目:

  • Redis 运行在 Docker 中,并开启 AOF 持久化。
  • Go client 负责创建普通、延迟、重试和不同优先级的任务。
  • Go server 负责消费 criticaldefaultlow 三个队列。
  • Asynqmon 提供队列、任务、重试、归档和服务端状态的可视化。
  • 真实 Redis 集成测试验证严格优先级,不只验证代码能编译。

扩缩容和多实例消费详见 多副本 Worker 部署与消费语义

项目固定使用 Asynq v0.26.0。Asynqmon 官方发布版较旧,本项目固定到官方仓库已升级 go-redis/v9 的 commit d1b889456d,并在同一个 Go module 中重新编译,使 Inspector 与 worker 最终统一使用 Asynq v0.26.0。

快速启动

前置条件:Docker、Docker Compose、Go 1.25 或更高版本,以及 curljq(完整验证使用)。go.mod 要求使用 Go 1.26.5 工具链,以包含 GO-2026-5856 的标准库修复;较旧 Go 在默认 GOTOOLCHAIN=auto 模式下会自动下载它。

# 1. 启动 Redis
make redis-up

# 2. 终端 A:启动 worker
make server

# 3. 终端 B:启动只读 Web UI
make monitor

# 4. 终端 C:投递完整演示任务
make demo

打开 http://127.0.0.1:8081。本机 8080 已被其他 Docker 服务占用,所以本项目使用 8081

本地只读 Asynqmon 队列概览:

Asynqmon 队列监控面板

停止 Redis:

make redis-down

docker compose down 会保留 redis-data volume。如需删除实验数据,明确执行 docker compose down -v

实验场景

# 一次投递完整场景:9 个优先级任务、1 个失败后重试任务、1 个 30 秒延迟任务
go run ./cmd/client -scenario demo

# 只投递优先级任务;故意按照 low -> default -> critical 的顺序入队
go run ./cmd/client -scenario priority

# 自定义一个任务
go run ./cmd/client \
  -scenario single \
  -queue critical \
  -name payment-001 \
  -duration 2s \
  -max-retry 5 \
  -timeout 10s \
  -retention 2h

# 创建延迟任务
go run ./cmd/client -scenario single -name delayed -process-in 30s

# 前两次执行失败,之后成功
go run ./cmd/client -scenario single -name retry-demo -fail-until 2 -max-retry 3

队列优先级

默认配置:

ASYNQ_QUEUES=critical=6,default=3,low=1
ASYNQ_STRICT_PRIORITY=false

加权优先级

ASYNQ_STRICT_PRIORITY=false 时,权重总和为 10。在三个队列都持续非空时,worker 理论上约 60% 从 critical、30% 从 default、10% 从 low 取任务。它不是单个任务级别的绝对顺序,而是避免低优先级队列永久饥饿的概率分配。

严格优先级

ASYNQ_CONCURRENCY=1 ASYNQ_STRICT_PRIORITY=true make server

严格模式永远先清空最高权重队列,再处理下一队列。高优先级持续有任务时,低优先级可能永久饥饿。make integration 会向真实 Redis 先后写入 lowdefaultcritical,再启动单 worker,并断言实际处理顺序为:

critical, critical, default, default, low, low

权重必须是正整数;空名称、重复名称、0/负数权重会在启动前报错。client 也会拒绝向 ASYNQ_QUEUES 未声明的队列投递,防止任务进入无人消费的队列。

Client 参数

cmd/client 暴露的参数:

CLI 参数 默认值 对应 Asynq Option 说明
-queue default Queue 目标队列,必须存在于 ASYNQ_QUEUES
-max-retry 3 MaxRetry 首次执行失败后的最大重试次数;总尝试次数最多为 1 + max-retry
-timeout 15s Timeout 单次 handler 最长运行时间
-deadline-in 0 Deadline 相对当前时间生成绝对截止时间;0 不设置
-process-in 0 ProcessIn 相对延迟;0 立即进入 pending
-process-at ProcessAt RFC3339 绝对执行时间;设置后覆盖 process-in
-retention 1h Retention 成功任务在 completed 状态保留时间;0 不保留
-unique-ttl 0 Unique 在 TTL 内按任务类型、payload、queue 去重;0 关闭
-task-id TaskID 显式任务 ID;只允许 count=1
-count 1 - 批量创建同类任务的数量
-duration 500ms payload 模拟工作耗时,最大 10 分钟
-fail-until 0 payload 前 N 次 handler 人为失败,用于观察 retry

Asynq 将 timeout、deadline、调度时间、retention 和 unique TTL 按整秒存储。client 会拒绝这些字段中的亚秒或非整秒值,避免 500ms 变成 0、1500ms 变成 1 秒后悄悄改变语义;模拟工作耗时 -duration 仍支持毫秒。

Asynq v0.26.0 的全部任务 Option:

Option 本项目状态 关键语义
Queue 已暴露 不设置时为 default
MaxRetry 已暴露 Asynq 原生默认 25;本项目显式设为 3
Timeout 已暴露 Asynq 原生默认 30 分钟;与 Deadline 同时存在时取更早者
Deadline 已暴露 绝对截止时间
ProcessIn 已暴露 相对调度时间;和 ProcessAt 冲突时最后一个 Option 生效
ProcessAt 已暴露 绝对调度时间
Unique 已暴露 best-effort 去重,不等于业务幂等
TaskID 已暴露 ID 冲突时入队失败
Retention 已暴露 只有设置后,成功任务才保留在 completed
Group 未暴露 需要 server 配置 GroupAggregator;本实验明确禁用聚合,避免任务停在 aggregating

Asynq 提供的是 at-least-once 执行语义。生产 handler 必须按业务键实现幂等,不能只依赖 Unique

Server 参数

cmd/server 显式构造完整的 asynq.Config。可配置项:

环境变量 默认值 Config 字段 说明
ASYNQ_CONCURRENCY 4 Concurrency 同时执行的 handler 数量
ASYNQ_QUEUES critical=6,default=3,low=1 Queues 队列及正整数权重
ASYNQ_STRICT_PRIORITY false StrictPriority false 加权随机;true 严格优先级
ASYNQ_TASK_CHECK_INTERVAL 1s TaskCheckInterval 所有队列为空时的轮询间隔;过低会增加 Redis 压力
ASYNQ_RETRY_DELAY 0s RetryDelayFunc 0 使用官方指数退避;大于等于 1 秒的整秒值使用固定间隔
ASYNQ_SHUTDOWN_TIMEOUT 15s ShutdownTimeout 优雅停机等待执行中任务的时间
ASYNQ_HEALTH_CHECK_INTERVAL 10s HealthCheckInterval Redis ping 周期
ASYNQ_DELAYED_TASK_CHECK_INTERVAL 2s DelayedTaskCheckInterval scheduled/retry 转 pending 的检查周期
ASYNQ_GROUP_GRACE_PERIOD 1m GroupGracePeriod 聚合等待窗口,Asynq 要求至少 1 秒
ASYNQ_GROUP_MAX_DELAY 0s GroupMaxDelay 聚合最大等待;0 表示无限制
ASYNQ_GROUP_MAX_SIZE 0 GroupMaxSize 单组触发聚合的最大任务数;0 表示无限制
ASYNQ_JANITOR_INTERVAL 8s JanitorInterval 清理过期 completed 任务的平均周期
ASYNQ_JANITOR_BATCH_SIZE 100 JanitorBatchSize 每批清理数量,过大可能产生长 Lua 操作
ASYNQ_LOG_LEVEL info LogLevel debug/info/warn/error/fatal

其余回调/对象字段也全部明确设置:

Config 字段 本项目设置 说明
BaseContext context.Background 每个 handler context 的根
RetryDelayFunc 默认 asynq.DefaultRetryDelayFunc ASYNQ_RETRY_DELAY>0 时改为固定间隔
IsFailure err != nil 非 nil error 计为失败;SkipRetry 仍为失败但直接归档
ErrorHandler 自定义日志函数 记录 task ID、queue、type、retry 次数和错误
Logger nil 使用 Asynq 默认 logger
HealthCheckFunc 自定义日志函数 Redis ping 失败时记录错误
GroupAggregator nil 明确禁用任务聚合;Group 相关时长不会生效

Redis 参数

环境变量 默认值 说明
REDIS_ADDR 127.0.0.1:6379 Redis 地址
REDIS_USERNAME Redis ACL 用户名
REDIS_PASSWORD Redis 密码
REDIS_DB 0 Redis logical DB
REDIS_DIAL_TIMEOUT 5s 建连超时
REDIS_READ_TIMEOUT 3s 读超时;-1 表示不超时,0 使用客户端默认
REDIS_WRITE_TIMEOUT 3s 写超时;-1 表示不超时,0 使用客户端默认
REDIS_POOL_SIZE 20 最大连接数

本地 Redis 只绑定 127.0.0.1,不对局域网暴露。镜像固定为 Redis 7.4.9-alpine 及其 digest,保证实验可复现。AOF 使用 appendfsync everysec,最多可能丢失约 1 秒尚未刷盘的数据;这是本地实验的性能/可靠性折中,不是生产高可用方案。

如需修改配置:

cp .env.example .env
# 修改 .env 后导入当前 shell
set -a; source .env; set +a

Asynqmon 参数与安全

环境变量 默认值 说明
MONITOR_ADDR 127.0.0.1:8081 Web UI 监听地址
MONITOR_READ_ONLY true 默认禁止 UI 修改任务/队列;本地实验需要管理操作时显式设为 false

Asynqmon 没有内置认证。本项目强制 MONITOR_ADDR 使用显式 loopback IP,拒绝 0.0.0.0、空 host 和 hostname;HTTP 层还会校验 Host、Origin 和 Sec-Fetch-Site,降低恶意网页攻击本机管理 API 的风险。默认只读;如确需本机管理功能,运行 MONITOR_READ_ONLY=false make monitor。生产共享部署应单独实现带认证、CSRF 防护和 TLS 的管理服务,而不是直接复用这个实验入口。

验证

# 单元测试和编译
make test

# 扫描标准库和 Go 依赖的已知漏洞
make vuln

# 真实 Redis 严格优先级测试
make integration

# 完整验证:Redis + 单测 + 集成测试 + server + client + UI/API
make verify

验证脚本不会删除 Redis volume。集成测试使用 DB 15,完整端到端验证强制连接 127.0.0.1:6379 的 DB 14,并覆盖所有 Redis/worker 环境变量,避免继承 .env 后误操作外部 Redis;两者都在测试前后执行 FLUSHDB,不要在这两个本地 DB 中放重要数据。端到端验证会编译真实二进制,使用固定 task ID,断言 completed/result/retry/scheduled/server 配置、只读与跨站保护,并通过 SIGTERM 验证优雅停机后 active 数量为 0。

目录结构

cmd/client/         任务生产者与演示场景
cmd/server/         worker 和完整 Server Config
cmd/monitor/        内嵌 Asynqmon Web UI
internal/config/    环境变量解析与严格校验
internal/tasks/     payload、任务构造器和 handler
integration/        真实 Redis 优先级测试
scripts/verify.sh   端到端验证
docker-compose.yml  Redis 容器

已知边界

  • 单 Redis 容器不是高可用架构;生产环境应评估 Redis Sentinel/Cluster、备份和持久化策略。
  • Asynqmon 前端上游较旧,但本项目通过统一 Go dependency、编译测试,以及 queues/servers/completed/scheduled/result/read-only API 契约验证当前实验兼容性。
  • 严格优先级可能饿死低优先级队列;生产默认更推荐加权模式,或为关键队列使用独立 worker 池。
  • 队列权重只影响取任务的顺序/概率,不限制单队列并发,也不提供 rate limit。
  • handler 超时依赖业务代码响应 context.Done();阻塞且不响应 context 的外部调用可能超过期望时间。

参考:Asynq 官方仓库Asynqmon 官方仓库

About

Production-oriented Asynq example in Go with Docker Redis, priority queues, retries, delayed jobs, Asynqmon, and multi-replica workers

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages