XREADGROUP + 消费者组 + XACK 是唯一生产级订阅方案,必须创建合法组、禁用NOACK、显式XACK原始ID、合理设置Count/Block,并依赖XPENDING/XCLAIM保障可靠性。

不能用 XREAD 做“订阅”,它不提供消费确认机制,消息一旦读出就脱离 Redis 管控——所谓“高可靠”根本无从谈起。真正能支撑生产级订阅的,只有 XREADGROUP + 消费者组 + 显式 XACK 这一套组合。
必须先创建消费者组,否则 XREADGROUP 会静默失败
很多人调用 XREADGROUP 返回空结果却查不出原因,根源在于没建组,或组名含空格/特殊字符(比如 "my group" 或 "group@prod"),Redis 不报错但拒绝路由消息。
- 首次运行必须执行
XGROUP CREATE mystream mygroup $ MKSTREAM:其中$表示从最新消息开始,MKSTREAM自动创建流(若不存在) - 重复执行同一
XGROUP CREATE会返回BUSYGROUP错误,Go 里要捕获并忽略,或提前用XINFO GROUPS mystream检查是否存在 - 组名只能是 ASCII 字母、数字、下划线、短横线;不能有空格、点、@、中文等
XREADGROUP 必须配 NOACK 或手动 XACK,二者不可兼得
XREADGROUP 默认行为是“读即入 pending”,这是可靠性的基石。但很多人误加 NOACK 参数,以为能提升性能,实则退化为不可靠的轮询模式。
- 不加
NOACK:消息读出后自动进入该消费者组的 pending 列表,崩溃后可通过XPENDING查看并用XCLAIM抢回 - 加
NOACK:跳过 pending 阶段,行为等同于XREAD,消息读完即“消失”,无重投能力 - Go 客户端中,
redis.XReadGroupArgs的NoAck字段为bool类型,设为true即启用NOACK,务必三思
每条消息必须显式 XACK,且 ID 必须用原始字符串
业务逻辑处理成功后漏掉 XACK,pending 消息就会越积越多,最终阻塞整个消费者组。更隐蔽的坑是传错 ID:Go 中 redis.XMessage.ID 是字符串,但拼接、取指针、用结构体字段直接传都会失败。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
立即学习“go语言免费学习笔记(深入)”;
-
XACK第三个参数是ids,类型为...string,必须传原始 ID 字符串,例如"1698765432100-0" - 不要传
&msg.ID、不要用fmt.Sprintf("%s", msg.ID)(看似一样,但底层可能含不可见字符) - 失败时应记录日志并触发告警,而不是忽略;可搭配
XPENDING定期扫描超时 pending 消息(如 >30s 未XACK)
Go 客户端要注意 Count 和 Block 的实际效果
社区封装常让人误以为 Count=1 就能“逐条处理”,但 Redis 实际返回的是最多 N 条,且 pending 消息和新消息混在一起,不控制好会导致积压。
-
Count控制单次最多返回几条,不是“保证返回几条”;若 pending 有 5 条而Count=1,只返回 1 条,其余继续挂 pending -
Block单位是毫秒,0表示不阻塞(立即返回),-1才是永久阻塞(部分旧版不支持,建议用time.Second * 30显式值) - 推荐设置
Count为 10–100(视吞吐量调整),配合合理Block时间,避免频繁空轮询或消息滞留
可靠性不来自某个命令多高级,而来自你是否愿意为每条消息写三行代码:XREADGROUP、业务处理、XACK。少一行,整套机制就塌一半;XPENDING 和 XCLAIM 不是备选方案,是必选项。

















