MyException - 我的异常网
当前位置:我的异常网» 开源软件 » Tomcat 装死原因分析

Tomcat 装死原因分析

www.myexceptions.net  网友分享于:2015-08-26  浏览:185次
Tomcat 假死原因分析

                                           Tomcat 假死原因分析报告

 

    最近监控服务发现有台tomcat 的应用出现了无法访问的情况 由于已做了集群,基本没有影响线上服务的正常使用。下面来简单描述该台tomcat当时具体的表现:客户端请求没有响应,查看服务器端tomcat 的java 进程存活,查看tomcat 的catalina.log ,没有发现异常,也没有error 日志.查看localhost_access.log 也没有最新的访问日志,该台tomcat 已不能提供服务。

    根据前面的假死表象,最先想到的是网络是否出现了问题,于是开始从请求的数据流程开始分析。由于业务的架构采用的是nginx + tomcat 的集群配置,一个请求上来的流向可以用下图来简单的描述。

            

           从上图可以看出,如果是网络的原因,可以从两个点进行分析。

    1、从前端到nginx的网络情况;

                    分析nginx上的access.log ,在其中一台上可以查出当时该条请求的访问日志,也就是说可以排除这段网络的问题。

    2、从nginx 到tomcat 的网络情况。

    分析tomcat 的访问日志localhost_acess.log 上无法查出该条请求的访问日志。可以怀疑是否网络有问题。就该情况,从该台nginx ping 了一下tomcat server ,均为正常,没有发现问题。既然网络貌似没有问题,开始怀疑是tomcat本身的问题,在tomcat本机直接curl 调用该条请求,发现仍然没有响应。到此基本可以断定网络没有问题,tomcat 本身出现了假死的情况。

    基于tomcat 假死的情况,开始分析有可能的原因。造成tomcat假死有可能的情况大概有以下几种:

    一、tomcat jvm 内存溢出

          分析当时的gc.log  ,


7581861.927: [GC 7581861.927: [ParNew
Desired survivor size 76677120 bytes, new threshold 15 (max 15)
- age   1:    5239168 bytes,    5239168 total
: 749056K->10477K(898816K), 0.0088550 secs] 1418818K->680239K(8238848K), 0.0090350 secs]

     没有发现有内存溢出的情况

    直接grep catalina.sh 也没有结果,证明没有发生内存溢出的情况 ,,这种假死可能可以排除。

    grep OutOfMemoryException catalina.sh

 

 

    二、jvm GC 时间过长,导致应用暂停

       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 显示,也不应该会暂停几分钟的情况,这种假死可能可以排除。

 

    三、load 太高,已经超出服务的极限

         当时top一下linux

         top 

 load average: 0.02, 0.02, 0.00
Tasks: 272 total,   1 running, 271 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.2%us,  0.2%sy,  0.0%ni, 99.6%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  32950500k total, 23173908k used,  9776592k free,  1381456k buffers
Swap: 33551744k total,      236k used, 33551508k free, 12320412k cached

         load 并不是高,,这种假死可能可以排除。

    四、应用本身程序的问题,造成死锁。

         针对这种情况,做了一下jstack,有少量线程处于TIMED_WAITING。


"Ice.ThreadPool.Client-75" daemon prio=10 tid=0x000000005c5ed800 nid=0x4cde in Object.wait() [0x0000000047738000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00002aab14336a10> (a IceInternal.ThreadPool)
        at IceInternal.ThreadPool.followerWait(ThreadPool.java:554)
        - locked <0x00002aab14336a10> (a IceInternal.ThreadPool)
        at IceInternal.ThreadPool.run(ThreadPool.java:344)
        - locked <0x00002aab14336a10> (a IceInternal.ThreadPool)
        at IceInternal.ThreadPool.access$300(ThreadPool.java:12)
        at IceInternal.ThreadPool$EventHandlerThread.run(ThreadPool.java:643)
        at java.lang.Thread.run(Thread.java:619)

        "ContainerBackgroundProcessor[StandardEngine[Catalina]]" daemon prio=10 tid=0x00002aacc4347800 nid=0x651 waiting on condition [0x00000000435f7000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1378)
        at java.lang.Thread.run(Thread.java:619)

"version sniffer" daemon prio=10 tid=0x00002aacc4377000 nid=0x645 in Object.wait() [0x0000000040f3c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00002aaaee20b7b8> (a java.lang.Boolean)
        at com.panguso.map.web.service.LocateServiceFactory$IpDataVersionSniffer.run(LocateServiceFactory.java:351)
        - locked <0x00002aaaee20b7b8> (a java.lang.Boolean)
        at java.lang.Thread.run(Thread.java:619)

"ReplicaSetStatus:Updater" daemon prio=10 tid=0x000000005d070800 nid=0x636 waiting on condition [0x0000000044001000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.mongodb.ReplicaSetStatus$Updater.run(ReplicaSetStatus.java:428)

 

    从jvm 堆栈信息可以看出,其中有可能出现线程锁死的情况为:IceInternal 和访问mongdb 的客户端 com.mongodb.ReplicaSetStatus$Updater类。针对这两种情况,看了一下源码,基本排除。

 

 

    五、大量tcp 连接 CLOSE_WAIT

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 

TIME_WAIT 48

CLOSE_WAIT 2228

ESTABLISHED 86

 

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

 


CLOSE_WAIT 从上面的图可以看出来,如果一直保持在CLOSE_WAIT 状态,那么只有一种情况,就是在对方关闭连接之后,服务器程序自己没有进一步发出ack 信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

由于咱们自己使用的是HttpClient ,并且遇到了大量CLOSE_WAIT 的情况。所以怀疑这个点可能出了问题。

查看了咱们的httpClient 的写法需要改正:

HttpGet get = new HttpGet(url.toString());

        InputStream ins = null ;

        try {

            HttpResponse response = excuteHttp(httpClient, get);

            // HttpResponse response = httpClient.execute(get);

 

            if (response.getStatusLine().getStatusCode() != 200) {

                 

                throw new MapabcPoiRequestException(

                        "Http response status is not OK" );

            }

 

这种写法意味着一旦出现非200 的连接,InputStream ins 根本就不会被赋值,这个连接将永远僵死在连接池里头.

解决方法:

if (response.getStatusLine().getStatusCode() != 200) {

                get.abort();

             

                throw new MapabcPoiRequestException(

 

                        "Http response status is not OK" );

            }

应该改为显示调用HttpGetabort ,这样就会直接中止这次连接,我们在遇到异常的时候应该显示调用,因为无法保证异常是在InputStream in 赋值之后才抛出。但是这种情况也是发生在httpClient 后端的服务出现了没有响应的情况,

 

 

 

 

 

 

 

 

 

 

 

 

 

文章评论

鲜为人知的编程真相
鲜为人知的编程真相
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
我的丈夫是个程序员
我的丈夫是个程序员
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
每天工作4小时的程序员
每天工作4小时的程序员
老程序员的下场
老程序员的下场
程序员和编码员之间的区别
程序员和编码员之间的区别
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
那些争议最大的编程观点
那些争议最大的编程观点
总结2014中国互联网十大段子
总结2014中国互联网十大段子
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
漫画:程序员的工作
漫画:程序员的工作
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
一个程序员的时间管理
一个程序员的时间管理
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
 程序员的样子
程序员的样子
代码女神横空出世
代码女神横空出世
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
编程语言是女人
编程语言是女人
程序员必看的十大电影
程序员必看的十大电影
旅行,写作,编程
旅行,写作,编程
程序员应该关注的一些事儿
程序员应该关注的一些事儿
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员的鄙视链
程序员的鄙视链
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
为什么程序员都是夜猫子
为什么程序员都是夜猫子
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Java程序员必看电影
Java程序员必看电影
如何成为一名黑客
如何成为一名黑客
我是如何打败拖延症的
我是如何打败拖延症的
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
中美印日四国程序员比较
中美印日四国程序员比较
10个调试和排错的小建议
10个调试和排错的小建议
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有