之前应该提过,我们线上架构整体重新架设了,应用层面使用的是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,是这个版本确实存在这个问题,原因是:

  1. 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:
  2. - timeout -> ERROR event
  3. - process ERROR (this is the new bit from r1746551)
  4. - try to finish handshake
  5. - need more data from client
  6. - register with poller for READ
  7. - wait for timeout
  8. - timeout ...
  9.  
  10. ... and around you go.

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

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

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

  然后换成Jetty

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

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

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

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

  现在没问题的代码是:

  1. if (socket.isHandshakeComplete()) {
  2. // No TLS handshaking required. Let the handler
  3. // process this socket / event combination.
  4. handshake = 0;
  5. } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
  6. event == SocketEvent.ERROR) {
  7. // Unable to complete the TLS handshake. Treat it as
  8. // if the handshake failed.
  9. handshake = -1;
  10. } else {
  11. handshake = socket.handshake(key.isReadable(), key.isWritable());
  12. // The handshake process reads/writes from/to the
  13. // socket. status may therefore be OPEN_WRITE once
  14. // the handshake completes. However, the handshake
  15. // happens when the socket is opened so the status
  16. // must always be OPEN_READ after it completes. It
  17. // is OK to always set this as it is only used if
  18. // the handshake completes.
  19. event = SocketEvent.OPEN_READ;
  20. }

  因为问题本就是因为握手正常建立的过程中被关闭造成的,只要判断改成如上,当握手是由于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. LINUX篇,设置MYSQL远程访问实用版

    每次设置root和远程访问都容易出现问题, 总结了个通用方法, 关键在于实用 step1: # mysql -u root mysql mysql> Grant all privileges o ...

  2. 04.SQLServer性能优化之---读写分离&数据同步

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 过段时间再继续写文章吧,本来准备把SQLServer一个系列写完的,最近状态很差很不好, ...

  3. PowerShell过滤文件中的重复内容

    Get-Content -Path E:\test11\data.txt | Sort-Object | Get-Unique 源文件: AA0001 2014-06-30 15:27:13.073 ...

  4. [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性

    回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...

  5. angularjs 依赖注入--自己学着实现

    在用angular依赖注入时,感觉很好用,他的出现是 为了"削减计算机程序的耦合问题" ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个 ...

  6. jsp页面无法识别el表达式的解决方案

    今天在写一个springmvc的小demo时,碰到一个问题,在jsp页面中书写为${user.username}的表达式语言,在浏览器页面中仍然显示为${user.username},说明jsp根本不 ...

  7. C++随笔:.NET CoreCLR之corleCLR核心探索之coreconsole(1)

    一看这个标题,是不去取名有点绕呢?或者是,还有些问题?报告LZ...你的标题取得有问题,是个病句!↖(^ω^)↗!!!先不要急,其实我今天带给大家的就是CoreCLR中的coreclr.其中它是在名字 ...

  8. C#创建、安装、卸载、调试Windows Service(Windows 服务)的简单教程

    前言:Microsoft Windows 服务能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这 ...

  9. java 字节流与字符流的区别

    字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢?实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作 ...

  10. Configure a bridged network interface for KVM using RHEL 5.4 or later?

    environment Red Hat Enterprise Linux 5.4 or later Red Hat Enterprise Linux 6.0 or later KVM virtual ...