【MQ消费异常】 runtimeId:order-center@app-8 Virtual host:turnright 队列:NOTIFICATION.V1.OrderDynamicInfoNotification#handleOrderDynamicInfoNotice 日志关键字:5de674a288774f6ea4d0e63ff5ab3154 异常类型:org.springframework.dao.QueryTimeoutException 异常信息:Command still hasn't been written into connection! Check CPU usage of the JVM. Check that there are no blocking invocations in async/reactive/rx listeners or subscribeOnElements method. Check connection with Redis node: 172.26.159.156/172.26.159.156:6379 for TCP packet drops. Try to increase nettyThreads setting. Node source: NodeSource [slot=11160, addr=null, redisClient=null, redirect=null, entry=null], connection: RedisConnection@2136349395 [redisClient=[addr=redis://172.26.159.156:6379], channel=[id: 0x880d1447, L:/172.17.0.2:58390 - R:172.26.159.156/172.26.159.156:6379], currentCommand=null, usage=1], command: (SET), params: [[106, 111, 98, 45, 108, 111, 99, 107, 58, 113, ...], [65, 68, 68, 69, 68, 58, 50, 48, 50, 53, ...], PX, 5000, NX] after 3 retry attempts; nested exception is org.redisson.client.RedisTimeoutException: Command still hasn't been written into connection! Check CPU usage of the JVM. Check that there are no blocking invocations in async/reactive/rx listeners or subscribeOnElements method. Check connection with Redis node: 172.26.159.156/172.26.159.156:6379 for TCP packet drops. Try to increase nettyThreads setting. Node source: NodeSource [slot=11160, addr=null, redisClient=null, redirect=null, entry=null], connection: RedisConnection@2136349395 [redisClient=[addr=redis://172.26.159.156:6379], channel=[id: 0x880d1447, L:/172.17.0.2:58390 - R:172.26.159.156/172.26.159.156:6379], currentCommand=null, usage=1], command: (SET), params: [[106, 111, 98, 45, 108, 111, 99, 107, 58, 113, ...], [65, 68, 68, 69, 68, 58, 50, 48, 50, 53, ...], PX, 5000, NX] after 3 retry attempts 是什么意思?
这个错误信息表明你的应用程序(order-center@app-8)在使用 Redisson 客户端库操作 Redis 时发生了超时异常。核心问题是:尝试执行一个 SET 命令(用于设置分布式锁)时,该命令甚至无法成功写入到与 Redis 服务器的网络连接中。
让我们分解一下关键信息:
异常类型:
org.springframework.dao.QueryTimeoutException: Spring 框架抛出的超时异常,通常由底层数据访问操作(这里是 Redis 操作)超时引起。org.redisson.client.RedisTimeoutException: 这是根本原因,来自 Redisson 客户端库,表明对 Redis 的操作超时了。
核心错误信息:
Command still hasn't been written into connection!: 这是最关键的一句。 它意味着 Redisson 客户端尝试发送一个 Redis 命令(这里是SET)到服务器,但这个命令在尝试写入底层的网络连接(Netty Channel)时就失败了,或者卡住了,以至于在超时时间内都没能成功写入。这通常发生在网络连接或客户端线程出现严重问题时。
Redisson 建议的排查方向:
Check CPU usage of the JVM.: 检查你的应用程序(order-center)所在 JVM 的 CPU 使用率是否过高。CPU 满载是导致 Netty 事件循环线程无法及时处理网络 IO 的最常见原因。Check that there are no blocking invocations in async/reactive/rx listeners or subscribeOnElements method.: 检查你的代码中,尤其是在异步、响应式或 RxJava 的回调函数(listeners)或subscribeOnElements方法中,是否存在阻塞操作(如耗时的同步 IO、复杂的计算、同步锁等待等)。这些阻塞操作会卡住 Netty 的 IO 线程,导致其他网络操作(包括发送 Redis 命令)无法进行。Check connection with Redis node: 172.26.159.156/172.26.159.156:6379 for TCP packet drops.: 检查应用程序服务器与 Redis 服务器172.26.159.156:6379之间的网络连接是否存在问题,特别是 TCP 丢包。网络不稳定或防火墙/安全组策略可能导致连接中断或数据包丢失。Try to increase nettyThreads setting.: 尝试增加 Redisson 配置中的nettyThreads参数值。这个参数控制 Redisson 用于处理网络 IO 的 Netty 线程数量。如果默认值太小,在高并发下可能成为瓶颈。
操作细节:
command: (SET), params: [[106, 111, 98, 45, 108, 111, 99, 107, 58, 113, ...], [65, 68, 68, 69, 68, 58, 50, 48, 50, 53, ...], PX, 5000, NX]- 这是尝试执行的 Redis 命令:
SET key value PX 5000 NX key: 字节数组[106, 111, 98, 45, 108, 111, 99, 107, 58, 113, ...]对应字符串job-lock:q...(一个分布式锁的 key)。value: 字节数组[65, 68, 68, 69, 68, 58, 50, 48, 50, 53, ...]对应字符串ADDED:2025...。PX 5000: 设置 key 的过期时间为 5000 毫秒(5秒)。NX: 仅当 key 不存在时才设置(实现锁的互斥性)。
- 这是尝试执行的 Redis 命令:
after 3 retry attempts: Redisson 内部重试了 3 次都失败了。Node source: ... connection: RedisConnection@2136349395 [redisClient=[addr=redis://172.26.159.156:6379]: 操作的目标是 Redis 节点172.26.159.156:6379。
总结与含义:
你的应用程序(order-center@app-8)在处理消息队列 NOTIFICATION.V1.OrderDynamicInfoNotification#handleOrderDynamicInfoNotice 中的消息时,需要获取一个名为 job-lock:q... 的分布式锁(使用 Redis 的 SET ... NX PX 命令实现)。然而,在执行这个关键的锁获取操作时,命令甚至无法通过网络连接发送出去,导致操作超时失败(RedisTimeoutException),进而触发了 Spring 的 QueryTimeoutException,最终导致 MQ 消息消费异常。
最可能的原因(按优先级):
- JVM CPU 使用率过高: 这是 Redisson 首要怀疑的原因。如果应用服务器的 CPU 被耗尽(接近 100%),负责网络 IO 的 Netty 线程可能无法获得足够的 CPU 时间片来及时处理发送 Redis 命令的任务。
- 代码中存在阻塞 Netty IO 线程的操作: 在异步回调、响应式流处理(如 Reactor/RxJava)的某些环节(尤其是
subscribeOnElements或类似监听器方法中),如果执行了耗时的阻塞操作(如同步数据库查询、文件读写、复杂计算、Thread.sleep、等待锁等),会阻塞处理网络事件的 Netty IO 线程。一个被阻塞的 IO 线程就无法处理发送 Redis 命令的请求。 - 网络问题: 应用程序服务器与 Redis 服务器
172.26.159.156:6379之间的网络连接不稳定,存在丢包、高延迟或瞬时中断。 - Netty 线程池配置不足: 默认的
nettyThreads配置可能不足以应对当前的并发负载。
下一步排查建议:
- 立即检查应用服务器 CPU: 查看
order-center@app-8所在服务器的 CPU 使用率历史记录(如 Grafana, Zabbix, top/htop)。如果持续或频繁接近 100%,这就是首要嫌疑。 - 检查 GC 日志: 分析 JVM 垃圾回收日志,看是否有长时间的 Full GC 停顿。长时间的 GC 也会导致所有线程(包括 Netty IO 线程)暂停。
- 检查线程堆栈: 在问题发生时或发生后,尽快获取
order-center@app-8的线程堆栈转储(jstack或通过 APM 工具)。重点分析名为redisson-netty-*的线程的状态。 如果这些线程大部分处于RUNNABLE状态执行业务代码(而不是网络 IO),或者卡在某个锁(WAITING,BLOCKED)上,就说明有阻塞操作侵占了 Netty IO 线程。 - 检查网络:
- 在应用服务器上使用
ping、traceroute/mtr到172.26.159.156,检查延迟和丢包。 - 使用
redis-cli -h 172.26.159.156 -p 6379 --latency测试 Redis 命令的延迟。 - 检查服务器之间防火墙、安全组规则是否允许稳定通信。
- 查看 Redis 服务器本身的网络和负载情况。
- 在应用服务器上使用
- 审查代码: 仔细检查消息监听器
handleOrderDynamicInfoNotice方法及其调用的所有逻辑,特别关注在 Redisson 的异步方法回调、Reactor/RxJava 的subscribeOn/publishOn(尤其是使用不恰当的调度器时)、doOnNext、doOnSuccess等操作符内部,是否存在任何可能导致线程阻塞的操作。确保耗时或阻塞操作被正确地卸载到专用的线程池(如使用Schedulers.boundedElastic())。 - 调整配置(谨慎): 如果确认 CPU 和网络没问题,且代码审查没有发现明显阻塞点,但并发量确实很高,可以考虑在 Redisson 配置中适当增加
nettyThreads的数量(例如从默认的当前CPU核心数 * 2增加到当前CPU核心数 * 3或当前CPU核心数 * 4)。但这通常是治标,找到根本原因(CPU 或阻塞代码)更重要。
总之,这个错误强烈指向应用服务器端(JVM CPU 或代码阻塞 Netty 线程)或网络连接问题,而不是 Redis 服务端本身的问题(虽然也需要确认其健康状态)。优先排查应用服务器的 CPU 使用率和线程堆栈。