连服务器内部异常java连接重置,本质是TCP层收到RST包导致连接被强制中断,绝大多数情况不是Java代码写错了,而是网络设备、操作系统或中间件配置引发的。
先搞清楚java连接重置是怎么回事
很多同学一看到java.net.SocketException: Connection reset就慌了,觉得程序出大问题了,连接重置的意思是:通信双方之间有一条TCP连接,某一端突然发了RST包,强制拆掉了这个连接,另一端收到RST后,读数据会报Connection reset,写数据会报Connection reset by peer。
RST包不会凭空产生,一定是连接某一方的内核或应用主动发出的,所以排查方向就一句话:谁发的RST,它就是幕后黑手,它可能在你的服务器上,也可能在客户端那边,还可能在你完全没注意到的中间环节防火墙、负载均衡、云平台的安全组。
行业中相当一部分连接重置故障,最终都定位在网络设备或云服务商的空闲连接超时上,而不是应用代码本身,Java应用只是受害者,程序在报错时把问题原样抛给你,但它不知道是哪个环节切了这刀。
RST包的两种典型来源
先用一根时间线判断RST从哪来:
- 如果连接建立后立刻被重置,优先怀疑服务端端口没监听、防火墙拦截、安全组禁止访问。
- 如果连接存活了一段时间后突然重置,优先怀疑空闲超时、进程崩溃、GC停顿引发的心跳超时、连接池资源回收。
第一种情况比较好查,telnet或nc试一下端口通不通就有结论,第二种情况复杂,需要结合业务场景逐步排除。
连接重置和连接超时是两码事
有个细节常被混为一谈。Connection reset和Connection timed out机制完全不同:
- 连接重置:连接存在过,然后被RST强制中断,相当于通话进行到一半,对方直接把电话挂了。
- 连接超时:SYN包发出去没有响应,连接根本建立不起来,相当于拨号打过去,对面一直没接。
排查思路完全不同,如果你用的是老帖子里的超时排查方案来查连接重置,大概率白费功夫。
连服务器内部异常的常见触发场景
结合日常运维实际,多数Java连接重置故障集中在这几个场景里,对着看一眼你的情况属于哪一种。
Tomcat连接池被打满,新连接直接被拒
Tomcat的acceptCount和maxThreads如果设置不合理,并发一上来,系统会拒绝新的TCP连接,此时客户端看到的不一定是Connection refused,在某些网络栈组合下,表现为连接被重置。
排查Tomcat连接数是否打满:
curl -u admin:password http://localhost:8080/manager/status
或
jstack <tomcat_pid> | grep "http-nio" | wc -l
看到http-nio线程数接近maxThreads上限时,问题基本就定位了,业内专家指出,生产环境多数连接重置故障发生在连接池参数设置不合理之后,尤其是没有预留峰值余量的系统。
服务端GC停顿导致心跳超时
如果你的服务之间有长连接通信,比如Dubbo、gRPC或者自研的KeepAlive链路,出现Connection reset的频率会显著上升,原因是服务端发生Full GC时会短暂停顿,客户端认为连接已死,主动发送RST断开连接。
这种场景下的报错特征是有规律性你去看GC日志,每次连接重置的时间点前后,大概率有一次长时间的Full GC停顿,解决办法看后面参数调整一节。
云环境下的防火墙空闲超时
这是近年来连接重置最主要的原因,但很容易被忽略,云服务商普遍会对经过负载均衡或安全策略的闲置连接设置超时,比如某些云LB四层监听默认空闲超时约几十秒,如果你的应用心跳间隔长于这个超时值,连接就会被中间设备悄悄切断。
判断方法:把网络抓包工具挂在服务端网卡上,如果抓到对端IP是负载均衡或网关设备的RST包,而该设备并非业务节点,那就实锤了,这类问题在跨地域调用场景中尤其常见,运维排查时很容易误判为Java应用故障。
java连接重置怎么排查
如果你现在正被这个错误折磨,直接按下面的顺序来,每一步都有明确产出,别一上来就翻日志,先看系统的视角。
第一步:确认RST包从哪个方向来
在服务端执行:
tcpdump -i eth0 'tcp[13] & 4 != 0' -nn
抓取网卡上所有的RST包,观察源的IP和端口,如果RST来源是业务对端,说明是对方主动断开的;来源是防火墙或云网关的IP,问题就在中间链路;来源是服务器自身的IP,那是本地网络层或内核主动发的。
同时在客户端也抓一份对比,两边数据一对照,方向立刻清晰。
第二步:jstack查看线程状态
抓完包再看Java内部发生了什么:
jstack <pid> > thread_dump.txt
关注处于WAITING或TIMED_WAITING状态的大量线程,是否集中在某个业务方法的锁上,连接池耗尽时,很多线程会卡在获取连接的代码处。
第三步:netstat看连接状态分布
netstat -anp | grep <java_pid> | grep ESTABLISHED | wc -l
netstat -anp | grep <java_pid> | grep TIME_WAIT | wc -l
TIME_WAIT数量爆炸的话,短连接场景下很容易触发性能劣化,间接引发连接重置。
第四步:交叉验证应用日志
各种排查手段适用场景参考下表:
| 排查手段 | 适用场景 | 能看到什么 |
|---|---|---|
| tcpdump抓包 | 所有场景,首推 | RST包的来源IP、端口、时间点 |
| jstack线程快照 | 连接池耗尽、线程阻塞 | 线程卡在哪段代码、是否存在死锁 |
| netstat连接状态 | 连接数异常 | ESTABLISHED / TIME_WAIT / CLOSE_WAIT分布 |
| GC日志 | 停顿引发的连接中断 | Full GC次数、停顿时长、发生时间 |
| Spring Boot Actuator | 服务健康状态监控 | 内存、线程池、连接池实时指标 |
多数情况下,抓包加jstack两板斧就能锁死原因,剩下的交给时间来验证修复效果。
服务端配置与代码层面的修复思路
问题定位到了,接下来就是动手调整,按影响面从小到大排列。
内核参数调整
如果你确认服务端发送了RST,可能是内核认为连接异常,查看当前的keepalive参数:
sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_intvl
sysctl net.ipv4.tcp_keepalive_probes
默认tcp_keepalive_time=7200秒,即两个小时没数据才探测一次,对长连接应用来说太慢了,建议调整为600秒左右:
sysctl -w net.ipv4.tcp_keepalive_time=600
sysctl -w net.ipv4.tcp_keepalive_intvl=30
sysctl -w net.ipv4.tcp_keepalive_probes=3
改完写入/etc/sysctl.conf持久化。
Java层超时设置
代码里的connectTimeout和socketTimeout设置要匹配你所在网络的实际情况,内网调用可以收紧到几十毫秒,跨公网调用要尽量放宽。
HttpClient连接池中不设置connectionTimeToLive
或connectionTtl时,空闲连接可能被服务端或中间设备回收,下次使用时报连接重置,设置一下:
PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
.setConnectionTTL(TimeValue.ofSeconds(60))
.build();
同时把空闲连接校验机制打开,让连接在归还连接池前先做一次轻量验证。
应用侧健康检查与重连机制
在应用里增加连接有效性校验,读取数据前发送心跳或探测包,业务代码在捕获到Connection reset异常时,需要将其视为可重试异常处理,不能让一次网络抖动直接击穿业务。
具备条件的话,在服务注册发现层面增加健康检查接口,让负载均衡自动摘除异常节点。
关于服务器连接被重置的常见疑问
Q:连接重置一定是服务端的问题吗?
不一定是,客户端、服务端、中间链路设备都有可能是发起RST的一方,分析时优先确认RST包的源IP,让数据说话比凭经验猜测靠谱得多,很多案例最后都证明云端负载均衡的空闲超时设置了断连接,服务端完全无辜。
Q:java连接重置can’t happen是什么情况?
部分NIO框架在极端情况下会抛出这个UncheckedIOException,属于JDK内部对不可能路径的防御,理论上官方称为can’t happen的情形,但如果系统资源被耗尽,或JVM内部状态出现不一致,还是有可能触发,碰到这种异常,优先检查内存和文件描述符是否打满,因为堆栈里通常看不到直接的业务线索,这类故障需要结合服务端整体健康状态排查,单独看一段异常信息意义不大。
Q:连接重置,是因为服务器配置太低吗?
配置低导致的性能瓶颈确实是诱因之一,但不能只看配置高低,更大比例的情况是某类资源被耗尽,比如线程池满、连接数达到上限、文件描述符耗尽,服务器配置够不够,要看监控指标,不是看机器规格,先确认连接数峰值和线程使用率,再评估是否要升级配置,别让一台配置充足的服务器背黑锅。
整体来看,连服务器内部异常java连接重置的排查逻辑就是三步:抓包确认RST来源、看Java线程和连接池状态、调整网络参数和应用超时配置,把这三步走完,基本上能覆盖九成以上的场景,遇到Connection reset不要慌,先问一句谁发的RST包,答案就在里面。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/711682.html




