Tomcat进程假死问题排查
目录
- 1.网络
- 1.1 检查nginx的网络情况
- 1.2 检查tomcat的网络情况
- 2.Jvm内存溢出
- 2.1为什么会发生内存泄漏
- 2.2快速定位问题
- 2.3 jstack查看tomcat是否出现死锁
- 2.4 jstat查看gc运行情况
- 2.5 jmap获取内存快照
- 3. jvm GC 时间过长,导致应用暂停
- 4. load 太高,已经超出服务的极限
- 5. 大量tcp 连接 TIME_WAIT
- 5.2、保持和server的长连接:
- 5.3、 proxy_set_header 配置注意事项
- 6. tomcat长连接数超过最大连接数
1.网络
1.1 检查nginx的网络情况
更改nginx的配置,让该台nginx请求只转到本机器的出现问题的tomcat应用上面,在access.log里看是否有网络请求,结果可以查看到当前所有的网络请求,也就是说可以排除是网络的问题。
1.2 检查tomcat的网络情况
分析业务配置的tomcat访问日志xxxx.log上是否有日志访问记录,经过查询该台tomcat应用日志完全没有任何访问记录,由于我们的部署是本机的nginx转到本机的tomcat应用,所以可以排除不是网络问题。 到此基本可以断定网络没有问题,tomcat 本身出现了假死的情况。在tomcat的日志里有报过OutOfMemoryError的异常,所以可以肯定tomcat假死的原因是OOM
!
2.Jvm内存溢出
2.1为什么会发生内存泄漏
在我们学习Java的时候就知道它最为方便的地方就是我们不需要管理内存的分配和释放,一切由JVM自己来进行处理,当Java对象不再被应用时,等到堆内存不够用时JVM会进行GC处理, 清除这些对象占用的堆内存空间,但是如果对象一直被应用,那么JVM是无法对其进行GC处理的,那么我们创建新的对象时,JVM就没有办法从堆中获取足够的内存分配给此对象,这时就会导致OOM。 我们出现OOM原因,一般都是因为我们不断的往容器里存放对象,然而容器没有相应的大小限制或清除机制,这样就容易导致OOM。
2.2快速定位问题
当我们的应用服务器占用了过多内存的时候,我们怎么样才能快速的定位问题呢?要想快速定位问题,首先我们必需获取服务器JVM某时刻的内存快照。 Jdk里面提供了很多相应的命令比如:jstack,jstat,jmap,jps等等. 在出现问题后我们应该快速保留现场。
2.3 jstack查看tomcat是否出现死锁
可以观察到jvm中当前所有线程的运行情况和线程当前状态.
sudo jstack -F 进程ID
输出内容如下: 从上面的图我们可以看到tomcat进程里面没有死锁的情况,而且每个线程都处理等待的状态。这个时候我们可以telnet命令连上tomcat的端口查看tomcat进程是否有任务回应。这时发现tomcat没有任何回应可以证明tomcat应用已没有响应处理假死状态。
在thread dump中,要留意下面几种状态 死锁, • Deadlock(重点关注) 等待资源, • Waiting on condition(重点关注) • 等待获取监视器,Waiting on monitor entry(重点关注) • 阻塞,Blocked(重点关注) • 执行中,Runnable • 暂停,Suspended • 对象等待中,Object.wait() 或 TIMED_WAITING • 停止,Parked
2.4 jstat查看gc运行情况
2.5 jmap获取内存快照
Jdk自带的jmap可以获取内在某一时刻的快照
命令:
jmap -dump:format=b,file=heap.bin file:保存路径及文件名 pid:进程编号(windows通过任务管理器查看,linux通过ps aux查看)
dump文件可以通过MemoryAnalyzer分析查看,网址:http://www.eclipse.org/mat/,可以查看dump时对象数量,内存占用,线程情况等。
3. jvm GC 时间过长,导致应用暂停
查看gc.log回收时间,以下为例子:
7581088.402: [Full GC (System) 7581088.402: [CMS: 661091K->669762K(7340032K), 1.7206330 secs] 848607K->669762K(8238848K), [CMS Perm : 34999K->34976K(58372K)], 1.7209480 secs] [Times: user=1.72 sys=0.00, real=1.72 secs]
最近的一次full gc 显示,也不应该会暂停几分钟的情况,这种假死可能可以排除。
4. load 太高,已经超出服务的极限
使用top 命令查看资源使用情况,都在合理范围,排除。
5. 大量tcp 连接 TIME_WAIT
Linux:
使用 ss -s 命令查看 tcp 链接状态, 发现TIME_WAIT 1800+, 有点高,需要修改。
打开 sysctl.conf 文件,修改以下几个参数:
[root@web01 ~]# vim /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_fin_timeout = 30
开启tcp_tw_reuse 和 tcp_tw_recycle 需要timestamps的支持,而且这些配置一般不建议开启,但是对解决TIME_WAIT过多问题有效果。谨慎操作!!!
然后又发现,nginx 没有开启长连接。
当使用nginx作为反向代理时,为了支持长连接,需要做到两点:
- 从client到nginx的连接是长连接
- 从nginx到server的连接是长连接
Windows:
netstat -ano -p tcp netstat -ano | find "ESTABLISHED"
5.1、保持和client的长连接:
[root@web01 ~]# vim /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_fin_timeout = 30
1)keepalive_timeout
语法:
keepalive_timeout timeout [header_timeout];
第一个参数:设置keep-alive客户端连接在服务器端保持开启的超时值(默认75s);值为0会禁用keep-alive客户端连接;第二个参数:可选、在响应的header域中设置一个值“Keep-Alive: timeout=time”;通常可以不用设置;
注:keepalive_timeout默认75s,一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为120s或者300s;
2)keepalive_requests:
keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
大多数情况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。但是,对于一些QPS比较高(比如超过10000QPS,甚至达到30000,50000甚至更高) 的场景,默认的100就显得太低。
简单计算一下,QPS=10000时,客户端每秒发送10000个请求(通常建立有多个长连接),每个连接只能最多跑100次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭。同样意味着为了保持QPS,客户端不得不每秒中重新新建100个连接。因此,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。
5.2、保持和server的长连接:
nginx访问后端默认都是用的短连接(HTTP1.0)
为了让nginx和后端server(nginx称为upstream)之间保持长连接,location中有两个参数需要设置:
http { server { location / { proxy_http_version 1.1; proxy_set_header Connection ""; } } }
5.3、 proxy_set_header 配置注意事项
在当前级别的配置中没有定义 proxy_set_header 指令时,这些指令从上级继承。
如果当前级别的配置中已经定义了 proxy_set_header 指令,在上级中定义的proxy_set_header 指令在当前级别都会失效。
举个例子:
http { ... proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; upstream example.com_test { server 127.0.0.1:8080; keepalive 16; } server { server_name example.com; location ^~ /test/ { proxy_set_header test test; proxy_pass http://example.com_test; } } }
这里后端服务器不能从 Header 中获取到 X-Real-IP。location ^~/test/ 中的proxy_set_header会覆盖上面的配置。
正确的做法,在location 中重复配置一遍:
http { ... proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; upstream example.com_test { server 127.0.0.1:8080; keepalive 180; } server { server_name example.com; location ^~ /test/ { proxy_set_header test test; proxy_set_header Host $host; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://example.com_test; } } }
6. tomcat长连接数超过最大连接数
发现tomcat 使用的是默认配置
tomcat默认最大连接数(线程数)200个,默认每一个连接的生命周期2小时(7200秒),tomcat使用http 1.1协议,而http1.1默认是长连接。tomcat接受处理完请求后,socket没有主动关闭,因此如果在2小时内,请求数超过200个,服务器就会出现上述假死现象。
解决办法:
(1)检查代码,及时断开socket
(2)修改tomcat配置文件,修改最大连接数(增大)
(3)修改linux的TCP超时时间(socket生命周期)限制
到此这篇关于Tomcat进程假死问题排查的文章就介绍到这了,更多相关Tomcat进程假死问题排查内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!