写点什么

超时与线程池的坑

用户头像
ES_her0
关注
发布于: 2021 年 05 月 17 日

近日我负责的后端服务出现了一些异常状况:有一些异步方法就是不执行。日志上看不到报错,我看了机器的整体监控,CPU idle 才 10%不到,似乎没有任何问题。只有一点比较奇怪,线上的节点有极少一部分的线程池 100%了,绝大多数线程池只在 5%左右徘徊,我一开始以为这个监控数据或者图标出了 bug,因为对应容器的 CPU 很正常,没什么负担。


后来经过高人指点,发现了确实是线程池的问题。首先需要知道的是,线程池 100%不一定与 CPU 的占用率正相关,有些任务比较轻量的话,即使 JVM 所有线程都在运行,CPU 也一样毫无波澜。其次,因为线程池 100%,导致有一些方法不是没有执行,而是延迟特别高,因为我们使用 callerRun 的拒绝策略,就是当线程池饱和时交给主线程去执行,在我们的场景下,相当于单线程去执行所有的任务,肯定很慢。


随即我去线上做了一次 jstack,看了一下线程的 dump 信息,我看大部分线程都在执行一个 http 远程调用。好像问题很快就会水落石出了,就跟着线程 dump 的调用栈看了下去,我们的代码里使用的一个第三方库会去远程更新一些时区的配置信息,里面使用的 httpClient 没有配置默认的超时时间:

final int connectTimeout = connectTimeoutProperty != null ? Integer.parseInt(connectTimeoutProperty) : 0;
final int readTimeout = readTimeoutProperty != null ? Integer.parseInt(readTimeoutProperty) : 0;
复制代码

这两项如果没有主动配置那就默认为 0,关于 connectTimeout 其 Java doc 里面是这么描述的:

Sets a specified timeout value, in milliseconds, to be used when opening a communications link to the resource referenced by this URLConnection. If the timeout expires before the connection can be established, a java.net.SocketTimeoutException is raised. A timeout of zero is interpreted as an infinite timeout.

看最后一句:A timeout of zero is interpreted as an infinite timeout.也就是说,如果因为网络问题他去请求的这个地址连接失败,就会一直等待连接,无限等待下去。所以最大 100 的线程池全部在无限等待,为什么只有少数节点出现呢,可能确实是因为当时的网络原因导致建连失败。


这是挺可怕的一件事。第三方的库,藏了一个这样的配置,还给你一个最危险的默认值。

用户头像

ES_her0

关注

还未添加个人签名 2018.03.21 加入

还未添加个人简介

评论

发布
暂无评论
超时与线程池的坑