之前应该提过,我们线上架构整体重新架设了,应用层面使用的是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的更多相关文章

  1. MySQL关于exists的一个bug

    今天碰到一个很奇怪的问题,关于exists的, 第一个语句如下: SELECT ) FROM APPLY t WHERE EXISTS ( SELECT r.APPLY_ID FROM RECORD ...

  2. 由一个bug引发的SQLite缓存一致性探索

    问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...

  3. Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?

    Win10左下角菜单打不开,好痛苦,点击右下角的时间也没反应,各种不爽,折磨了我好几天,重装又不忍心,实在费劲,一堆开发环境要安装,上网找了很多方法都不适用.今天偶然解决了,仔细想了下,难道是Win1 ...

  4. 你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG

    这篇文章并不在我的 underscore 源码解读计划中,直到 @pod4g 同学回复了我的 issue(详见 https://github.com/hanzichi/underscore-analy ...

  5. 标准模板库(STL)的一个 bug

    今天敲代码的时候遇到 STL 的一个 bug,与 C++ 的类中的 const 成员变量有关.什么,明明提供了默认的构造函数和复制构造函数,竟然还要类提供赋值运算符重载.怎么会这样? 测试代码 Tes ...

  6. 是uibutton跟tableviewcell同步使用一个bug

    这个问题是uibutton跟tableviewcell同步使用一个bug,不关delay一点毛事,证据就是点击事件没问题,so,搜到一个方法解决了这个问题.uibutton分类symbian2+ios ...

  7. 在chrome下-webkit-box布局的一个bug

    chrome,也就是webkit内核下作的检测, chrome版本是40, -webkit-box这种布局在移动端用的比较多,主要是因为pc端的浏览器内核参差不齐. 因为在写HTML的时候看上了-we ...

  8. 关于MySQL count(distinct) 逻辑的一个bug【转】

    本文来自:http://dinglin.iteye.com/blog/1976026#comments 背景 客户报告了一个count(distinct)语句返回结果错误,实际结果存在值,但是用cou ...

  9. 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug

    开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...

随机推荐

  1. 一看就懂的ReactJs入门教程-精华版

    现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...

  2. SQL必备知识点

    经典SQL语句大全 基础 1.说明:创建数据库.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备份数据的 device.说明:创建新表crea ...

  3. Matlab 绘制三维立体图(以地质异常体为例)

    前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...

  4. 学习笔记之MVC级联及Ajax操作

    由于刚转型到MVC,MVC的架构模式很多不是很清楚,比如今天就想做个级联的操作,因为之前的ASP.NET的方式是通过:控件-->添加事件-->后台编写级联事件进行触发,但是这个MVC就不同 ...

  5. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  6. 终端mysql Operation not permitted错误解决方案

    前言 前段时间装mysql,就遇到了ln: /usr/bin/mysql: Operation not permitted的错误,网上好多方法都过时了,下边是我的解决方法 原因 这是因为苹果在OS X ...

  7. 【热门技术】EventBus 3.0,让事件订阅更简单,从此告别组件消息传递烦恼~

    一.写在前面 还在为时间接收而烦恼吗?还在为各种组件间的消息传递烦恼吗?EventBus 3.0,专注于android的发布.订阅事件总线,让各组件间的消息传递更简单!完美替代Intent,Handl ...

  8. 基于RN开发的一款视频配音APP(开源)

    在如今React.ng.vue三分天下的格局下,不得不让自己加快学习的脚步.虽然经常会陷入各种迷茫,学得越多会发现不会的东西也被无限放大,不过能用新的技术作出一些小项目小Demo还是会给自己些许自信与 ...

  9. 【Java每日一题】20170106

    20170105问题解析请点击今日问题下方的"[Java每日一题]20170106"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  10. JAVA 分页工具类及其使用

    Pager.java package pers.kangxu.datautils.common; import java.io.Serializable; import java.util.List; ...