文档章节

Tomcat一个BUG造成CLOSE_WAIT

drv
 drv
发布于 2017/01/06 17:35
字数 1173
阅读 83
收藏 0

    之前应该提过,我们线上架构整体重新架设了,应用层面使用的是Spring Boot,前段日子因为一些第三方的原因,略有些匆忙的提前开始线上的内测了。

    然后运维发现了个问题,服务器的HTTPS端口有大量的CLOSE_WAIT:

    我的第一反应是Spring boot有Bug,因为这个项目分为HTTP和HTTPS两种服务以JAR的形式启动的,而HTTP的没有问题,同时,老架构的服务在Tomcat中以HTTPS提供服务也没有问题,我当时认为这大致上可以判断为Socket层面应该是没有问题的,于是我开始分析Spring Boot的代码。

    经过调试和分析(过程如果有机会,再整理一篇),虽然没有找到引起这个现象的原因,但是发现一个规律,所有出现问题的连接org.apache.tomcat.util.net.NioEndpoint的内部类SocketProcessor中doRun方法中,握手状态一直处于handshake == SelectionKey.OP_READ,监听一直不会关闭。

    虽然,到这一步看上去问题应该出现在Socket层面,但是我还是觉得应该是Spring Boot的,因为Spring Boot引用的Tomcat的处理这部分功能的代码虽然是内嵌的(tomcat-embed-core-8.5.4),但是和完整版并没有什么区别,而完整版是没有这个问题的。

    然后,因为两个原因,我决定继续排查,直接去提ISSUE了:一、需要大量时间分析相关代码才能保证解决这个问题不出现其他问题;二、可以肯定这不是我们新架构和开发的问题。于是我去github提了个Issue,问题在:https://github.com/spring-projects/spring-boot/issues/7780,然而第二天果不其然的被建议让我去给Tomcat提Issue:

    虽然我依然认为这是在甩锅,但是我并没有什么能证明这不是Tomcat问题的证据。于是我又看了看代码,试图证明一下 ,然而并没有找到。

    终于,我去给Tomcat提了个Bug,https://bz.apache.org/bugzilla/show_bug.cgi?id=60555,回复指向了另外一个BUG,是这个版本确实存在这个问题,原因是:

The problem occurs for TLS connections when the connection is dropped after the socket has been accepted but before the handshake is complete. The socket ended up in a loop:
- timeout -> ERROR event
- process ERROR (this is the new bit from r1746551)
- try to finish handshake
- need more data from client
- register with poller for READ
- wait for timeout
- timeout ...

... and around you go.

    好吧,既然Tomcat接盘了,咱也不多说啥了,但是我对比了一下本地的类包的代码和r1746551的代码,并且调试了一下以后,发现并不是他说的代码造成的,因为我调试了r1746551的代码依然没有解决问题。不过,线上环境的问题倒是有了个勉强可以接受的解决办法,内嵌的Tomcat换成内嵌的Jetty,果然是没有问题了。

   现在gradle.build中排除spring-boot-starter-web对内嵌Tomcat的引用:

compile('org.springframework.boot:spring-boot-starter-web:1.4.0.RELEASE'){
    exclude module: "spring-boot-starter-tomcat"
}

    然后换成Jetty

[group: 'org.springframework.boot', name: 'spring-boot-starter-jetty', version: '1.4.0.RELEASE'],

    至于,提给Tomcat的那个问题,我抽空再仔细琢磨琢磨在去接着提,不过刚才测试升级了一下版本果然是没问题了。

    调试了一下,果然感觉解决问题的并不是他写的r1746551,下面是我看代码的时候发现的,直接解决问题的部分,并不包含在r1746551中,原来有问题的部分:

                        if (socket.isHandshakeComplete() || event == SocketEvent.STOP) {
                            handshake = 0;
                        } else {
                            handshake = socket.handshake(key.isReadable(), key.isWritable());
                            // The handshake process reads/writes from/to the
                            // socket. status may therefore be OPEN_WRITE once
                            // the handshake completes. However, the handshake
                            // happens when the socket is opened so the status
                            // must always be OPEN_READ after it completes. It
                            // is OK to always set this as it is only used if
                            // the handshake completes.
                            event = SocketEvent.OPEN_READ;
                        }

    现在没问题的代码是:

                        if (socket.isHandshakeComplete()) {
                            // No TLS handshaking required. Let the handler
                            // process this socket / event combination.
                            handshake = 0;
                        } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                                event == SocketEvent.ERROR) {
                            // Unable to complete the TLS handshake. Treat it as
                            // if the handshake failed.
                            handshake = -1;
                        } else {
                            handshake = socket.handshake(key.isReadable(), key.isWritable());
                            // The handshake process reads/writes from/to the
                            // socket. status may therefore be OPEN_WRITE once
                            // the handshake completes. However, the handshake
                            // happens when the socket is opened so the status
                            // must always be OPEN_READ after it completes. It
                            // is OK to always set this as it is only used if
                            // the handshake completes.
                            event = SocketEvent.OPEN_READ;
                        }

    因为问题本就是因为握手正常建立的过程中被关闭造成的,只要判断改成如上,当握手是由于socket建立失败造成的就会走到close方法,而原本的判断方法是无法做到的,于是问题解决了。至于这段代码的位置,我在开始就说了,嘿嘿。。。,如果有我看漏的地方,大家务必告诉我。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

© 著作权归作者所有

drv

drv

粉丝 2
博文 57
码字总数 75382
作品 0
东城
架构师
私信 提问
大量的close_wait状态的tcp链接造成tomcat假死,急,求大神

如题。项目在正常运行过程中,已经正常运行很久,突然发现项目访问不了,重启了tomcat才可以正常访问。查了数据,正常的时候tcp的close_wait数一直是几个左右,在项目死掉的时间点上close_w...

小龙0啦啦
2018/06/14
3K
7
【2016-06-13】一次BufferReader没有close引发的血案

Hive-Web是我司Web端查询Hive数据的服务,功能上比较简单,用户在Web上写一个SQL,Hive-Web将SQL提交到后端的服务执行查询,得到结果的hdfs路径,然后通过hadoop的fs读取文件,将其返回给用户...

rathan0
2016/06/13
550
0
从问题看本质: 研究TCP close_wait的内幕

最近遇到的一个关于socket.close的问题,在某个应用服务器出现的状况(执行netstat -np | grep tcp): tcp 0 0 10.224.122.16:50158 10.224.112.58:8788 CLOSEWAIT tcp 0 0 10.224.122.16:37655......

pior
2016/03/30
319
0
netstat -anlp|grep 8080

[hhhhh@Huan-VM68 bin]$ netstat -anlp|grep 8080 (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) tcp 0 ......

lhanoo
2012/03/06
19
0
服务器的close wait状态缓慢增加问题

登录服用的tomcat,然后每次启动之后,每天总会增长一部分TCP close wait状态(一天大概40左右),就是服务器连接被动关闭,服务器没有发FIN状态给客户端。一般过几周之后,就会因为close w...

ChangeZ
2016/04/01
512
0

没有更多内容

加载失败,请刷新页面

加载更多

哈希

第一个只出现一次的字符的位置

Garphy
25分钟前
4
0
Centos7.7之离线安装kubectl

Centos7.7,kubernates-1.13.5. 我的Centos7.7上已经安装了kubernates 1.13.5,但是没有kubectl命令,手动安装 浏览器中访问https://storage.googleapis.com/kubernetes-release/release/sta......

克虏伯
27分钟前
4
0
redis原理及应用

一、redis来源 二、数据类型 三、主流的应用场景 四、特性 五、补充 一、 redis来源 redis作者:Salvatore Sanfilippo (antirez),男,意大利人. 需求:一个访客信息追踪网站,网站可以通过...

天子剑毅
35分钟前
3
0
12_多线程

12_多线程 wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器(释放锁)。 notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个...

行者终成事
40分钟前
5
0
图片的切换功能

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <style type="text/css"> * { margin: 0; padding: 0; ......

zhengzhixiang
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部