BRPOPLPUSH卡住需查慢日志和CLIENT LIST:若slowlog中频繁出现BRPOPLPUSH retry_queue retry_queue 0且cmd字段长期卡在brpoplpush,结合LRANGE发现积压超5000,即为单点重试队列阻塞。

BRPOPLPUSH在Redis里卡住怎么确认
直接看 Redis 的慢日志和客户端连接状态就能定位。如果 BRPOPLPUSH 命令执行时间超过 100ms,说明它正在排队等前面积压的任务——这不是网络问题,是队列本身没分片,所有失败任务全挤在一个 list 里。
用 redis-cli --latency 测延迟,再跑 redis-cli slowlog get 10 看最近慢命令;重点找 BRPOPLPUSH retry_queue retry_queue 0 这类自循环调用。如果返回里频繁出现这个,基本就是重试队列单点阻塞了。
- 别只查
INFO clients,要看CLIENT LIST里每个连接的cmd字段是否长期卡在brpoplpush - 用
LRANGE retry_queue 0 10手动查积压量,超 5000 就得警觉——BRPOPLPUSH 的阻塞时长跟长度正相关 - asynq 默认的
retry_queue没做分片,不能靠加 worker 数缓解,必须改队列结构
worker空转但队列有任务怎么查
这不是 Redis 问题,是 asynq 的消费模型缺陷:多个 worker 共享一个 list key 时,Redis 不保证公平出队,短任务快的机器会持续抢到活,长任务慢的机器反而饿死。
验证方法很简单:在两个 worker 启动时分别指定不同 asynq.RedisClientOpt{Addr: "redis://host:port/1"} 和 asynq.RedisClientOpt{Addr: "redis://host:port/2"},再观察日志里 task processed 是否均匀分布。如果不均匀,说明原来都在争同一个 db=0 的 list。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 不要依赖
Concurrency参数来“均衡”,它只控制单个 worker 内 goroutine 数,不解决跨实例争抢 - 检查是否用了默认
asynq.DefaultServeMux,混注册的 handler 可能因某个 type panic 导致整个 mux 卡住,其他 type 也跟着无法调度 - 用
asynq.QueueName("queue-a")显式绑定,比靠默认名更可控
payload解析panic没日志怎么补救
asynq 默认不记录原始 payload,json.Unmarshal 失败直接 panic,错误栈里只有 invalid character 'n' looking for beginning of value 这种泛化信息,根本看不出是哪个字段错了。
必须在入口加预校验:不是等 asynq 自己解,而是在 asynq.HandlerFunc 里先 json.Unmarshal + validator 结构体 tag 校验,失败就 return error,让 asynq 走失败路径而不是崩掉。
- 别在
OnFailure回调里只打err.Error(),要显式写入task.Payload的 string 形式(用string(task.Payload)),否则日志里看不到原始数据 - 禁止用
fmt.Printf或log.Println记录,它们不带 traceID,线上排查时找不到上下文 - 如果 payload 很大(>10KB),记录前截断,避免日志系统被撑爆
channel缓冲队列莫名阻塞怎么诊断
用 make(chan Task)(无缓冲)或缓冲太小(如 make(chan Task, 10))是高频原因。阻塞不报错,只表现为上游协程 stuck 在 ch ,pprof 看 goroutine stack 会卡在 <code>chan send。
查法很直接:用 go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2 抓 goroutine 列表,搜 chan send 关键字。如果几十上百个都停在这,说明消费者跟不上,或者 consumer goroutine panic 后没重启。
- 别用
len(ch)判断是否满——它只是瞬时快照,且对 channel 没实际意义 - 发送端必须配
select { case ch ,否则背压会传导到 HTTP handler 层 - consumer 里别漏
recover(),一个 panic 就会让整个 for-range 循环退出,后续任务全积压
真实线上问题往往不是单点故障,而是多个设计盲区叠加:比如 payload 校验缺失导致 worker 频繁 panic,panic 又让 channel 消费者退出,消费者退出后缓冲区迅速填满,最终所有上游卡在 send。排查时得一层层剥,从最外层的 HTTP 超时往里查,而不是一上来就怀疑 Redis 性能。

















