Spring Boot基于Rest Api的服务长时间运行偶发无响应问题求助
Java Rest服务长时间运行偶发无响应问题排查方案
可能触发原因
- JVM线程池耗尽:内置web容器(Tomcat/Undertow/Jetty)的工作线程被慢请求、死锁、阻塞操作全部占满,新请求进入等待队列后无法被处理,此时端口仍处于监听状态但无业务逻辑执行,因此没有新日志生成
- JVM GC异常停顿:堆内存泄漏导致老年代占满,持续触发Full GC,元空间/直接内存溢出,都会导致JVM长时间停顿,无法响应任何业务请求
- 系统资源耗尽:Java进程打开的文件句柄数超过系统ulimit限制,无法为新的请求分配文件描述符;系统内存占满后swap分区被大量使用,进程响应速度骤降
- 框架配置不合理:web容器的最大连接数、最大工作线程数、等待队列长度配置过低,并发请求量超过阈值后新请求直接被丢弃或阻塞
- 全局锁死锁:拦截器、过滤器、公共工具类中的全局同步锁出现死锁,所有请求执行到对应逻辑时全部阻塞
可落地排查步骤
问题发生时先不要重启服务,第一时间采集以下信息定位根因:
JVM层面排查
- 执行
jps获取Java服务的进程PID - 执行
jstack [PID] > jstack_$(date +%Y%m%d%H%M).log导出线程栈,分析是否存在死锁,是否有大量HTTP工作线程处于BLOCKED/WAITING状态 - 执行
jstat -gcutil [PID] 1000 10查看GC状态,确认是否存在Full GC频繁、GC耗时占比过高的情况 - 执行
jmap -dump:format=b,file=dump_$(date +%Y%m%d%H%M).hprof [PID]导出堆转储文件,事后分析是否存在内存泄漏
操作系统层面排查
- 执行
lsof -p [PID] | wc -l统计进程打开的文件句柄数,和ulimit -n的返回值对比,确认是否触及句柄上限 - 执行
top -p [PID]查看进程CPU、内存占用率,执行free -h确认系统内存、swap分区占用情况 - 执行
netstat -anp | grep [服务端口]统计TCP连接状态,确认是否存在连接数溢出的情况
应用层面排查
- 检查web容器配置,确认最大工作线程数、最大连接数、等待队列长度是否符合业务并发需求,例如SpringBoot内置Tomcat的默认最大线程数仅为200,高并发场景下很容易耗尽
- 排查全局拦截器、过滤器、公共依赖中是否存在未释放的锁、未设置超时时间的同步IO操作
通用解决方案
- 给JVM添加GC日志、堆转储自动生成参数:
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./,方便后续问题回溯 - 调整系统ulimit参数,将进程最大打开文件句柄数调整为65535或更高,永久生效可修改
/etc/security/limits.conf配置 - 根据服务器配置和业务并发量,调大web容器的最大连接数、工作线程数、等待队列长度参数
- 优化慢请求逻辑,所有外部依赖调用(第三方接口、缓存、消息队列)必须设置合理的超时时间,避免阻塞工作线程
内容的提问来源于stack exchange,提问作者Ruhul Amin
相关产品推荐
相关产品推荐

