一次由IDEA配置引发的Redis连接问题

发布于:2025-03-20 ⋅ 阅读:(22) ⋅ 点赞:(0)

偶然的发现

书接上回 『谁动了我的SunEC?——记深夜排查SSL握手失败的惊魂一小时』,某日下午,A同学又找到我确认个接口的实现逻辑,说话间点击了IDEA的启动按钮开始启动服务。

项目启动过程中我猛然发现A同学IDEA的控制台怎么出现了错误,瞬间引起了我的注意

我:“嗯? 你这怎么启动还报错, 啥配置不对吗 ? “

A同学:”我这最近一直是这样,不过不影响使用,接口啥的都能正常访问。“

我:“具体啥错误,你没分析看看 ?”

A同学:”是一个Redis连接的错误,好像是Redis连接不上。"

我想了想:“嘶,我们现在没有使用Redis了才对呀,两级缓存那个Redis配置我都关掉了,怎么还会去连接Redis。我本地启动也没报错呀。”

我:“先说你的接口问题吧,这个等接口讨论完再看下“

此处省略接口逻辑的讨论过程

问题排查

我说:“现在再看下刚才那个Redis连不上的问题”

A同学说:“就是很奇怪,我看配置啥也没问题,但是不知道为啥启动就会去尝试连接Redis,之前我也尝试改了些配置,打断点看了下,没找到问题”

我说:“我看看”,随后坐到A同学工位上。看了下启动错误,如下(完整错误太长,此处保留关键的部分)

2025-03-10 16:38:33.190 [] [boundedElastic-1] WARN  org.springframework.boot.actuate.redis.RedisReactiveHealthIndicator - Redis health check failed
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to localhost:6379
	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1689) ~[spring-data-redis-2.7.18.jar:2.7.18]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoSubscribeOnCallable] :
	reactor.core.publisher.Mono.subscribeOn(Mono.java:4589)
	org.springframework.boot.actuate.redis.RedisReactiveHealthIndicator.getConnection(RedisReactiveHealthIndicator.java:57)
Error has been observed at the following site(s):
	*__Mono.subscribeOn ⇢ at org.springframework.boot.actuate.redis.RedisReactiveHealthIndicator.getConnection(RedisReactiveHealthIndicator.java:57)
	|_     Mono.flatMap ⇢ at org.springframework.boot.actuate.redis.RedisReactiveHealthIndicator.doHealthCheck(RedisReactiveHealthIndicator.java:52)

核心信息就是 “Redis health check failed”

再往前看日志输出的位置,发现是来自org.springframework.boot.actuate.redis.RedisReactiveHealthIndicator这个类,这不是spring-boot-actuator中的类么。

我说:“这应该是spring-boot-actuator进行组件健康检查了,是不是你的配置文件开启了这个配置。”

说话间,我打开A同学工程里的application.yaml文件,发现配置都是禁用的,尝试进行了一些配置的调整验证发现并没有效果。

这就有意思了,配置没开启,但是却执行了,版本啥的不一致导致的 ?

目测一番没发现问题,那就只能顺着异常栈去分析了,看看出错的位置在哪里,也就是这行

RedisReactiveHealthIndicator.doHealthCheck(RedisReactiveHealthIndicator.java:52),然后再往下分析,哪里调用了这个方法,也就是异常栈里面最底下的那个Caused by, 结果如下

Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: localhost/127.0.0.1:6379

Caused by: java.net.ConnectException: Connection refused: no further information
	at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) ~[?:?]
	at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:777) ~[?:?]
	at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:334) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]

这怎么还是一个线程直接拉起来的,这下完了,从哪起的这个线程,异常栈里是看不见的,触发调用这个健康检查的代码位置也没找到。

这时候A同学轻声道:“其实就是启动报个错,也不影响啥”。

这不是我的风格,必须干掉它。

深入分析

既然堆栈上没有直接线索,那就剩下分析源码了,于是我从 RedisReactiveHealthIndicator 中输出的Redis health check failed 这行日志作为切入点,再结合 RedisReactiveHealthIndicator.doHealthCheck(RedisReactiveHealthIndicator.java:52)找到了如下代码

在这里插入图片描述

那么,可以确定的就是这行代码被执行了,那么剩下就是往回找,找到调用这个方法的最外层入口就行了。

经过一通分析和DEBUG,最终串联了这些类

在这里插入图片描述

稍有眉目

最后定位到,项目启动了JMX监控(如上图所示的类),执行了组件的健康检查,但是程序里并没有启动相关服务,这个是从哪启动的。

难道是加了什么JVM启动参数 ?立即查询启动的VM参数发现并没有配置相关内容,那还有可能是哪里加的呢 ?

突然,想到IDEA启动项目时,会在控制台输出所有的JVM参数,看一下就知道了,随即查看IDEA控制台输出的java启动命令,果然,在里面发现了可疑配置参数

-Dcom.sun.management.jmxremote 
-Dspring.jmx.enabled=true 
-Dspring.liveBeansView.mbeanDomain 
-Dspring.application.admin.enabled=true 
"-Dmanagement.endpoints.jmx.exposure.include=*"

但是,我们并没有添加这些参数,这是从哪里来的 ? IDEA自己加的么

然后,我在自己电脑上启动了下同样查看了控制台参数,发现并没有输出相关配置

然后看了下A同学的IDEA版本,2024 标准版本,而我的是2022.2.3社区版

难道是IDEA标准版自己加了这些参数 ?

发现问题

然后查看IDEA Edit Configurations 检查相关配置,最终发现了如下配置,这个配置默认是没勾选了,也就是会增加一系列JMX的参数,最终导致启动时候加载到了 actuator 中的 JMX模块的代码,从而触发了Redis的健康检查代码。

在这里插入图片描述

随后,勾选 Disable JMX endpoints 启动项目,一切正常。