tomcat假死探索与解决,tomcat connector配置

24 篇文章 2 订阅
5 篇文章 0 订阅

上一篇博客介绍了解决oom导致的程序假死,这里探索解决程序假死的问题。

排除oom

首先怀疑还是内存溢出导致的,但是:
没有有异常日志
没有dump文件生成
gc.log,证明jvm堆内存还多着呢,如下:
[PSYoungGen: 246097K->8179K(240640K)] 1093342K->869809K(4172800K), 0.0522768 secs] [Times: user=0.18 sys=0.02, real=0.05 secs] 
minorgc后,新生代200多M,用了8M;jvm整体4G内存,也就用了900M。
明显说明tomcat挂掉的时候堆中还剩余很多内存。排除jvm的oom错误,此时heap还有很大空间,排除oom的可能性。
这里补充几个查看jvm状态的指令
jmap -heap pid :查看某进程堆状态
jstat -gc pid : 查看进程的gc统计
jstack pid >> head.log : 将当前进程中线程的状态输出到head.log文件中。
gc.log的分析可参考:https://blog.csdn.net/h2604396739/article/details/87815823

第一反应是我修改代码导致的,挨个回滚并测试,发现毫无用处。

猜想一:linux的oom-killer导致

什么是linux的oom-killer:
Linux的malloc分配内存,不是一次到位的真分配了指定大小的物理内存,而是先承诺你,实际用到的时候才去系统分配,如果刚好那个时候内存不够了,就会触发oom-killer。
oom_killer(out of memory killer)是Linux内核的一种内存管理机制,在系统可用内存较少的情况下,内核为保证系统还能够继续运行下去,会选择杀掉一些进程释放掉一些内存。通常oom_killer的触发流程是:进程A想要分配物理内存(通常是当进程真正去读写一块内核已经“分配”给它的内存)->触发缺页异常->内核去分配物理内存->物理内存不够了,触发OOM。
一句话说明oom_killer的功能:
当系统物理内存不足时,oom_killer遍历当前所有进程,根据进程的内存使用情况进行打分,平均消耗内存越高得分越高,然后从中选择一个分数最高的进程,杀之取内存。
关于oom-killer导致的进程死亡可以参考下面两篇文章
https://blog.csdn.net/liu251/article/details/51181847
http://www.kuqin.com/linux/20120701/321430.html

work@v-web-01 ~ $ dmesg | grep java
[13598416.310265] INFO: task java:2320 blocked for more than 120 seconds.
[13598416.313090] java            D ffffffff8168a850     0  2320   1265 0x00000080
[17958045.456044] java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
[17958045.457931] java cpuset=/ mems_allowed=0
[17958045.459905] CPU: 3 PID: 2100 Comm: java Not tainted 3.10.0-514.26.2.el7.x86_64 #1
[17958045.617487] [ 1703]     0  1703   629830    19224      97        0             0 java
[17958045.641509] [ 6262]  1001  6262  2677222  1870645    3883        0             0 java
[17958045.649750] Out of memory: Kill process 6262 (java) score 936 or sacrifice child
[17958045.651089] Killed process 6262 (java) total-vm:10708888kB, anon-rss:7482580kB, file-rss:0kB, shmem-rss:0kB

work@v-bosszpbiweb-01 ~ $ dmesg | egrep -i -B100 'killed process' 结果:
[17958045.649750] Out of memory: Kill process 6262 (java) score 936 or sacrifice child
[17958045.651089] Killed process 6262 (java) total-vm:10708888kB, anon-rss:7482580kB, file-rss:0kB, shmem-rss:0kB

运维人员执行 cat  /var/log/messages | grep oom-killer 发现linux最近并没有进行oom的killer,最近的一次是一周前,也就是我优化之前,当时确实占用了6G内存。
确定不是linux oom killer,下面两个角度也可以佐证:
1)如果是linux的oom killer,那么tomcat进程应该会挂掉,tomcat进程应该不再存在,现在现象是进程还在,但是服务彻底停止,程序假死.
2)系统内存是8G,我的jvm的heap设置的4G,现在仅仅用了不到2G,所以如果除了我的服务还有别的服务,并且相比之下我的服务整体占用内存较多情况下,才有可能linux系统执行oomkiller,杀掉我的进程。但实际情况是该机器上仅仅有我的tomcat,并且小于系统的8G内存,所以排除oom-killer的可能性。


猜想二:数据库连接池耗尽导致的程序假死

借鉴:https://blog.csdn.net/chang_yushun/article/details/88929625
以前查一次,会将所有的数据保存到内存中,所以不再需要connection,现在因为oom的问题,不再缓存,所以会采取每请求每连库,这样对数据库连接池的要求就很高了,
但是配置的最大连接数还是以前的20,然后如果超过了连接池的限制,那么所有的请求阻塞,然后会一直累积,然后进一步导致程序假死。修改该逻辑的时候确实考虑到会增加mysql的压力和链接,
也去找dba确认了库的压力,库没有压力过大的问题,也考虑到连接的问题,所以找dba确认了连接数的问题,dba反馈300百左右的连接都不是问题,实际连接都是低于100的。
检查了一下代码中库连接的配置,将代码中关于连接池的maxActive配置从20修改为100,也调大空闲连接数,然后测试效果:
测试发现还是会存在程序假死的情况,但是有所改善,以前一天能挂三次,现在一天也就挂一次,说明增大连接池有一定效果,但是肯定不是导致tomcat假死的根本原因。


猜想三:大量并发请求阻塞导致的假死

netstat -ano | grep TIME_WAIT | wc -l
这个正常的情况下的值是低于100的,但是模拟大量请求的时候发现,该值会飙升到3000左右。对linux来说,这个值到几万都没有问题,但是对tomcat来说就不一定了。
所以猜测可能是大量请求阻塞导致的tomcat假死,修改tomcat的请求连接配置。原本的server.xml中的配置就是默认的,如下
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
修改后如下:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"  acceptorThreadCount="2" 
connectionTimeout="20000"  enableLookups="false"  
maxThreads="200"    acceptCount="1000"    maxConnections="10000"   
minSpareThreads="100"  tcpNoDelay="true"   />

修改io类型:protocol="org.apache.coyote.http11.Http11NioProtocol"  四种IO模型:BIO、NIO、NIO2、APR,默认bio,修改成nio,我项目是bi系统,所以存在大量的数据需要传输,使用nio模式,提高性能 

处理连接的线程配置:acceptorThreadCount:用于接收连接的线程的数量,默认值是1。一般这个值需要改动的时候是因为该服务器是一个多核CPU,如果是多核CPU 一般配置为 2.

maxThreads:用于接收和处理client端请求的最大线程数,tomcat底层将采取线程池的方式来处理客户端请求,此参数标识这线程池的尺寸.maxThreads意味着tomcat能够并发执行request的个数.此值默认为200事实上"200"个线程数,已经足够大了.本人的线上环境为maxThreads=200.对于NIO模式下,maxThreads参数应该由CPU核心数和你的线程执行内容决定,如果是查询数据库或者需要进行磁盘的IO需要cpu进行等待,那么可以适当调大该值;但是如果线程的执行内容就是进行计算或者是别的需要一直占用cpu的操作,太大的值,并不能提升NIO性能,反而会使性能下降,因为线程切换(CS)将会占据CPU的大量时间。

minSpareThreads:线程池中,保持活跃的线程的最小数量,默认为10。

首先明确一下连接与请求的关系:连接是TCP层面的(传输层),对应socket;请求是HTTP层面的(应用层),必须依赖于TCP的连接实现;一个TCP连接中可能传输多个HTTP请求。

有关连接(三次握手四次挥手就是一个完整的连接)数的配置:
maxConnections: "10000",tomcat并发处理最大tcp连接数
acceptCount:acceptCount即是此队列的容量,如果队列已满,此后所有的建立链接的请求(accept),都将被拒绝。默认为100。在高并发/短链接较多的环境中,可以适当增大此值;当长链接较多的场景中,可以将此值设置为0.

单个连接的能接受的请求个数(单个连接中能接受的最大请求数):
maxKeepAliveRequests: 每个TCP连接接受最大的Http请求数目,当处理一个keep alive请求达到这个最大值,Tomcat关闭这个连接,设置1为失效任何keep alive请求,对于BIO高并发,四层负载平衡和NoSSL情况需要失效;对于SSL APR/NIO 7层负载平衡需要激活,设置为-1是不限制,缺省为100。

如果想要真正理解tomcat connector参数的含义,需要理解tomcat的通信模式:reactor:https://blog.csdn.net/h2604396739/article/details/81123143

修改后的效果:目前已经稳定运行一周时间,没有再出现tomcat假死的场景。

假死原因分析见下篇文章:https://blog.csdn.net/h2604396739/article/details/91377054

2019.09.20补充:上面的默认配置protocol="HTTP/1.1",tomcat对应bio的说法不正确,笔者用的tomcat版本已经是8.5.而tomcat从8.0开始,protocol="HTTP/1.1"  默认IO为nio了。

那下面哪个参数发生了实际作用?
enableLookups="false"   与并发请求量无关
maxThreads="200"  默认200 未修改
acceptCount="1000"  默认100--》1000
maxConnections="10000"   nio默认10000
minSpareThreads="100"  default 10
tcpNoDelay="true" default true
acceptorThreadCount="2"  1--》2

maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
maxConnections :最大连接数,bio下等于maxThreads,nio下默认10000
到底怎么接收请求的?
bio下,因为每连接每线程,所以此时先达到maxthreads,然后缓存请求,最大接收请求数为maxThreads+acceptCount
nio下,先达到maxConnections,然后缓存acceptCount,超过拒绝

问题:
tomcat 7: protocol="HTTP/1.1"  默认IO为Bio,所以此时maxConnection的大小为默认的maxthreads 200,很好解释上面的程序假死现象?
tomcat 8: protocol="HTTP/1.1"  默认IO为nio,那么此时maxConnection的大小为10000还是maxthreads 200,按照定义”对于NIO和NIO2,默认值为10000“,应该是10000,但是protocol没有显式声明nio,所以也有可能是等于maxthreads200
我用的tomcat版本8.5
如果是200也可以解释很好解释上面的程序假死现象。
如果是10000,那不应该出现假死,而且即使出现了假死,上面的配置也仅仅是 acceptCount="1000"  默认100--》1000发生了作用,这个跟maxConnections(10000)相比也是杯水车薪,为什么能解决假死?

希望知道的大神帮忙解答。。。

参考:

bio nio 和aio区别可以参考文章;可以参考https://blog.csdn.net/h2604396739/article/details/82534253
http://tomcat.apache.org/tomcat-7.0-doc/config/http.html---tomcat connector配置官网说明
https://blog.csdn.net/m0_37797416/article/details/83505663----tomcat中server.xml中Connector各个参数的意义
https://www.cnblogs.com/kismetv/p/7806063.html#t2--详解tomcat的连接数与线程池
https://blog.csdn.net/binglong_world/article/details/80748520----杜绝假死,Tomcat容器设置最大连接数

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值