Redisson 4.6.1 数字命令收到 PONG,检查 RESP3 布尔响应错位
Redisson 4.6.1 跳过已完成命令的 RESP3 布尔响应时,可能留下未消费字节。4.8.0已补齐该分支;先确认协议及响应,再区分解码错位与重试重复执行。
Redisson 4.6.1 在负载或超时附近出现 Unable to decode data,SCARD、HLEN 本应得到数字,却报 java.lang.String incompatible with java.lang.Long。如果相邻日志还出现 PONG 等属于其他命令的响应,应检查连接上的响应是否已经错位,而不只是更换某个键的 codec。
其中一条已明确的故障路径是:连接收到 RESP3 布尔响应,原命令的 promise 又已经完成,解码器在跳过这个迟到响应时没有消费完整字节。它有两个前提,不能仅凭 String 转 Long 的异常就断定已经命中。
布尔响应少跳过几个字节,后面的命令也会受影响
RESP3 的布尔值以 #t 或 #f 加 CRLF 表示。4.6.1 的正常解码支持这种类型,但用于丢弃已完成命令响应的 skipDecode() 缺少 # 分支。它读掉类型字节后便返回,剩余内容仍留在输入缓冲区,后面的命令与响应可能无法继续配对。
超时或已取消的保活 ping 可以让响应到达时对应 promise 已经完成;这解释了为什么故障可能集中在连接受阻、超时附近。它不意味着 PING 自己会返回 RESP3 布尔值,也不意味着任何一次客户端超时都必然破坏响应流。
先确认实际协议、所用命令和中间代理是否可能带来布尔响应。普通的 Valkey 8.0.2 常用命令组合未必会发出这类响应;同样的压力和重连条件,也可能完全不经过这个分支。只有异常文本,没有相邻原始响应与命令配对信息,仍不足以确定根因。
采用完整处理 RESP3 跳过分支的正式版本
正式 Redisson 4.8.0 已补上跳过布尔响应的处理。项目允许升级时,选择包含该修补且与现有框架集成兼容的正式版本,更新锁定依赖并确认运行进程真正加载了新 JAR。修补的是 RESP 解码器,不是业务数据格式;无需把 Redis 中的数值键批量改成字符串。
不能只修改 IntegerReplayConvertor,把收到的任意字符串强行解析成数字。若 SCARD 实际拿到 PONG,即便忽略异常,也没有取回集合数量;这种改法会把连接状态错误藏进业务结果。
修补后的代码会在 # 分支消费完整响应行,但它不能证明另一条协议错误、代理响应或 codec 配置也已恢复。已经发生异常的请求,应按业务编号检查处理结果,不能把异常统一解释成“服务端未执行”。
把响应错位和重复执行分开检查
另一个容易混淆的表现是:连接阻塞超过重试间隔后,INCR 等命令的结果出现跳号。原请求已经在途中,又被重新发送,可能产生重复执行;这不需要出现任何 RESP3 布尔响应,也不证明解码器错位。
因此,更新解码器后仍要按原有幂等约定核对写入。不要通过无限增加重试次数去掩盖异常,更不要清空缓存或删除键来“重置连接”。若需要采集解码日志,应在受控窗口保留同一通道的命令、响应、超时和协议设置,隐藏认证信息与业务值;原始响应日志可能包含敏感数据,不适合长期全量开启。
在隔离 Redis 或 Valkey 实例中,覆盖正常响应、超时后迟到响应,以及业务实际使用的 RESP3 命令;核对返回类型与对应操作,不能只看异常条数下降。写命令另核对实际执行次数和业务状态。回退依赖也不会撤销此前已经执行的命令,应保留原版本、配置和有影响的操作记录。


