共计 5457 个字符,预计需要花费 14 分钟才能阅读完成。
本文首发于 vivo 互联网技术 微信公众号
链接:https://mp.weixin.qq.com/s/8f34CaTp–Wz5pTHKA0Xeg
作者:vivo 官网商城开发团队
家喻户晓,Oracle JDK 是 Java 语言的相对权威,很多时候 JDK 与 Java 语言近似一个概念。但咱们始终要放弃捕风捉影的精力,敢于质疑。本文记录了一次线上 troubleshoot 实战,蕴含问题剖析、解决并提交 Oracle JDK bug 的外围过程。
一、背景景象
总之 就是某零碎上线后 CLOSE_WAIT 数量随着工夫减少而大量减少,继续触发多个告警。
二、剖析定位过程
部署了一个节点,用来复现之前呈现的问题。
Step1 问题聚焦
先查看到底是哪些 IP 之间的连贯产生了大量 CLOSE_WAIT,另外零碎还会波及调第三方,总之要确认连贯建设的单方。
执行命令:
netstat -np | grep tcp|grep “CLOSE_WAIT”
后果:
(ps:xxx、yyy、zzz 均无含意,基于信息安全思考,屏蔽掉 ip)。
tcp 3547 0 10.107.17.xxx:34602 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:59088 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:58028 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:51962 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 3563 0 10.107.17.xxx:46962 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:34608 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:46496 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:50774 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:59904 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:40208 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:41064 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:36994 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 3547 0 10.107.17.xxx:45080 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 6235 0 10.107.17.xxx:60966 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:56178 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 3547 0 10.107.17.xxx:39922 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:43270 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:40926 zzz.202.32.242:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:44472 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 2891 0 10.107.17.xxx:43036 zzz.202.32.241:443 CLOSE_WAIT 19819/java
........
........
tcp 38 0 10.107.17.xxx:33472 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:51976 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:57788 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:35638 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:43778 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:46418 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:49914 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:49258 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:48718 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:51480 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:59816 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:49266 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:50246 yyy.12.230.115:443 CLOSE_WAIT 19819/java
tcp 38 0 10.107.17.xxx:39324 yyy.12.230.115:443 CLOSE_WAIT 19819/java
总之:
yyy.12.230.115
zzz.202.32.241
zzz.202.32.241
这个三个 IP 是导火索。
Step2 问题剖析
这三个 IP 具体是谁?具体是申请了哪个接口?
临时无奈间接获知!最间接的导火索临时断了线索。接着从侧面开始查看更多信息,
-
JVM 信息
内部资源、线程 什么的都看了, 未发现显著异样
-
抓包
要抓包获取更多线索了。对于很久没有碰过 TCP 层,有些吃力。
失去线索: 发现大量的 RST
那么是什么操作会导致 CLOSE_WAIT 呢?什么样的连贯导致大量 RST 呢 (可参考 RST 通常起因)?
Step3 代码剖析定位
运维大佬的帮助查问,得悉这三个 IP 是图片 CDN 服务。
至此,能够定位到具体代码逻辑,图片 CDN 申请能够排查代码。
仔细分析这部分源码后,揣测因为服务器 发动 URL 申请,申请不存在,导致抛出异样,然而 JDK 中却没有中央敞开 Socket。
javax.imageio.read(URL)
/**
* Returns a <code>BufferedImage</code> as the result of decoding
* a supplied <code>URL</code> with an <code>ImageReader</code>
* chosen automatically from among those currently registered. An
* <code>InputStream</code> is obtained from the <code>URL</code>,
* which is wrapped in an <code>ImageInputStream</code>. If no
* registered <code>ImageReader</code> claims to be able to read
* the resulting stream, <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching in the
* <code>ImageInputStream</code> that is created.
*
* <p> This method does not attempt to locate
* <code>ImageReader</code>s that can read directly from a
* <code>URL</code>; that may be accomplished using
* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
*
* @param input a <code>URL</code> to read from.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>input</code> is
* <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public static BufferedImage read(URL input) throws IOException {if (input == null) {throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
// 此处,建设 TCP 连贯!并且间接获取流, 因为流数据不存在,进入 cache 块,抛出!istream = input.openStream();} catch (IOException e) {throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = createImageInputStream(istream);
BufferedImage bi;
try {bi = read(stream);
if (bi == null) {stream.close();
}
} finally {istream.close();
}
return bi;
}
能够看到 JDK 并没有敞开 ImageIO.read(url) 代码中封装的 Socket 连贯!CDN 会申请超时敞开导致服务器处于 CLOSE_WAIT?限于网络教训无限,并不能 100% 确认我的想法。所以模仿下吧。
Step4 复现与模仿
依据零碎业务源码,疾速模仿:
public static void main(String[] args) throws InterruptedException {ExecutorService ex = Executors.newFixedThreadPool(100);
for (int i = 0; i < 5000; i++) {ex.execute(task());
}
}
/**
* @throws IOException
* @throws MalformedURLException
*/
private static Runnable task() {return new Runnable() {
@Override
public void run() {
// domain must exists,but file doesnot.
String vivofsUrl = "https://vivobbs.xx.yy.zz/wiwNWYCFW9ieGbWq/20181129/3a2adfde12cd328d81f965088890eeffff.jpg";
File file = null;
BufferedImage image = null;
try {file = File.createTempFile("abc", "jpg");
URL url1 = new URL(vivofsUrl);
image = ImageIO.read(url1);
} catch (Throwable e) {e.printStackTrace();
} finally {if (null != file) {file.delete();
}
if (null != image) {image.flush();
image = null;
}
}
}
};
}
抓包
TCP 查看
问题复现!
Step5 沟通后提报 bug
report 给 Oracle。
三、Oracle 沟通
提单之后,Oracle 跟我分割沟通。截取局部邮件内容,仅供参考。
已被驳回
四、疑点与有余
TCP 状态机的流转不够相熟透彻。导致一些问题不能从 TCP 状态机剖析推理,常识的全面精通须要一直进步。
更多内容敬请关注 vivo 互联网技术 微信公众号
注:转载文章请先与微信号:Labs2020 分割。