诊断由于 blocking socket 未设置 socket timeout 而引起的线程卡住( thread stuck)

Java 既有早期的 blocking IO (面向流的Input/output stream, 面向字符的 Reader/Writer), 又有之后加入的 NIO/ NIO 2 (Channel & Buffer). 很多应用都是使用的 早期的 blocking 的 IO. 不论是 blocking IO 还是 NIO 都要注意 socket timeout 问题, 如果不设置, 都会引起应用卡在 IO 的问题.

症状

发现有应用的各个服务器上的 tomcat 忙碌线程数差别比较大, 有的只有 2~3 个忙碌, 有的达到 20 多个.

检查

查看 tomcat 忙绿线程的长期数据, 发现只要重启之后, 就只会增加, 不会减少.
进一步做了几个 thread dump, 发现这些线程都停在读 socket 数据上. 如下:
rnoepncos-1459655_stratus_rno_ebay_com_8083_admin_raptorlog_threaddump_20191130_185218_24346_log_tdump_and_Slack_____Zheng_Wang___eBAY_SEC___7_new_items.png

分析

为什么会长期一直读? 是一直读同一个流, 还是每次做 dump, 正好线程又落在这个地方?
通过 ss -r 命令, 发现确实有这么多tcp连接都在. 进一步通过 ss -troi 命令查看到这些 socket 都已经好久没有读写数据了, 如下图:
xiatian_lvssshbastion1-3328412___.png
上图中 lastsnd, lastrcv, lastack 是最后一次发送/接受/确认距现在的毫秒数. 也就是这么久都没有数据传输了.

为什么这么久没数据传输还没关闭连接呢? 难道没有设置 socket timeout? 在 heap dump 中我们确实看到这些 socket 没有设置 timeout:
SELECT s.address.holder.hostName.toString(), s.timeout FROM java.net.SocksSocketImpl s WHERE (s.port = 443)
sjc-sreop-001.png

解决

客户端在连接 swift 的设置当中, 没有设置 timeout.

思考

  1. tomcat 忙碌线程数 的对于各个服务器的方差大小可以帮助审计这里问题;
  2. tocmat 忙碌线程数的长期趋势 只增不减 可以帮助审计这类问题;
  3. 发现好多线程停在 socketRead0 上面要怀疑是不是 socket timeout 设置是不是有问题;
  4. ss 命令可以发现每个 socket 的具体细节, 比如使用了什么拥堵算法, socket 拥有者的用户是谁, 进程是谁? 每个 socket 的 传输速率是多少, 接收了多少, 发送了多少, 最后一次发送/接收/ack 距现在多久.

标签: none

添加新评论