Tomcat一个BUG造成CLOSE_WAIT
之前应该提过,我们线上架构整体重新架设了,应用层面使用的是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
微信公众号:

Tomcat一个BUG造成CLOSE_WAIT的更多相关文章
- MySQL关于exists的一个bug
今天碰到一个很奇怪的问题,关于exists的, 第一个语句如下: SELECT ) FROM APPLY t WHERE EXISTS ( SELECT r.APPLY_ID FROM RECORD ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?
Win10左下角菜单打不开,好痛苦,点击右下角的时间也没反应,各种不爽,折磨了我好几天,重装又不忍心,实在费劲,一堆开发环境要安装,上网找了很多方法都不适用.今天偶然解决了,仔细想了下,难道是Win1 ...
- 你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG
这篇文章并不在我的 underscore 源码解读计划中,直到 @pod4g 同学回复了我的 issue(详见 https://github.com/hanzichi/underscore-analy ...
- 标准模板库(STL)的一个 bug
今天敲代码的时候遇到 STL 的一个 bug,与 C++ 的类中的 const 成员变量有关.什么,明明提供了默认的构造函数和复制构造函数,竟然还要类提供赋值运算符重载.怎么会这样? 测试代码 Tes ...
- 是uibutton跟tableviewcell同步使用一个bug
这个问题是uibutton跟tableviewcell同步使用一个bug,不关delay一点毛事,证据就是点击事件没问题,so,搜到一个方法解决了这个问题.uibutton分类symbian2+ios ...
- 在chrome下-webkit-box布局的一个bug
chrome,也就是webkit内核下作的检测, chrome版本是40, -webkit-box这种布局在移动端用的比较多,主要是因为pc端的浏览器内核参差不齐. 因为在写HTML的时候看上了-we ...
- 关于MySQL count(distinct) 逻辑的一个bug【转】
本文来自:http://dinglin.iteye.com/blog/1976026#comments 背景 客户报告了一个count(distinct)语句返回结果错误,实际结果存在值,但是用cou ...
- 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug
开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...
随机推荐
- app开发外包注意事项,2017最新资讯
我们见过很多创业者,栽在这app外包上.很多创业者对于app外包这件事情不是特别重视,以为将事情交给app外包公司就完事了,实际上不是的.无论是从选择app外包公司还是签订合同.售后维护等各方面都有许 ...
- nodejs利用http模块实现银行卡所属银行查询和骚扰电话验证
http模块内部封装了http服务器和客户端,因此Node.js不需要借助Apache.IIS.Nginx.Tomcat等传统HTTP服务器,就可以构建http服务器,亦可以用来做一些爬虫.下面简单介 ...
- vs2010静态链接MFC库报链接错误
由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 自定义鼠标光标cursor
通过css属性 Cursor:url()自定义鼠标光标. {cursor:url('图标路径'),default;} url是自定义鼠标图标路径 default指的是定义默认的光标(通常是一个箭头), ...
- BPM的魅力何在?
BPM(Business Process Management , 企业流程管理平台) 是带动企业流程自动化的帮 手,也是最能忠实反应出企业作业流程问题症结的系统工具,在管理上,BPM可以让管理者利用 ...
- 记:MySQL 5.7.3.0 安装 全程截图
前言: 下一个班快讲MySQL数据库了,正好把服务器里面的MySQL卸了重装了一下. 截个图,作为笔记.也正好留给需要的朋友们. 目录: 下载软件 运行安装程序 安装程序欢迎界面 许可协议 查找更新 ...
- Android NDK debug 方法
最近又频繁遇到 NDK 的错误,记录一下debug调试的一些经验,以备后续查看 一般来说,在Android Studio中的Monitor中将过滤器的 LOG TAG 设置为 "DEBUG& ...
- [转]Java常用工具类集合
转自:http://blog.csdn.net/justdb/article/details/8653166 数据库连接工具类——仅仅获得连接对象 ConnDB.java package com.ut ...
- Linux+apache+mono+asp.net安装教程
Linux+apache+mono+asp.net安装教程(CentOS上测试的) 一.准备工作: 1.安装linux系统(CentOS,这个就不多讲了) 2.下载所需软件 http-2.4.4.ta ...