震惊!线上4台机器同时OOM,到底发生了什么?
The following article comes from 码海 Author 码海
昨天晚上突然短信收到 APM 大量告警。紧接着运维打来电话告知线上部署的四台机器全部 OOM (out of memory,内存不足),服务全部不可用,赶紧查看问题!
图片来自 Pexels
问题排查
第一时间想到 Dump 当时的内存状态,但由于为了让线上尽快恢复服务,运维重启了机器,导致无法 Dump 出事发时的内存。所以我又看了下我们 APM 中对 JVM 的监控图表。
不看不知道,一看吓一跳,从 16:00 开始应用中创建的线程居然每时每刻都在上升,一直到 3W 左右,重启后(蓝色箭头),线程也一直在不断增长,正常情况下的线程数是多少呢,600!
问题找到了,应该是在下午 16:00 左右发了一段有问题的代码,导致线程一直在创建,且创建的线程一直未消亡!
问题定位了,应该就是这个配置导致的!(线程上升的时间点和发布时间点完全吻合!),于是先把这个新加的配置给干掉上线,上线之后线程数果然恢复正常了。
那 evictExpiredConnections 做了什么导致线程数每时每刻在上升呢?这个配置又是为了解决什么问题而加上的呢?于是找到了相关同事来了解加这个配置的前因后果。
还原事发经过
最近线上出现不少 NoHttpResponseException 的异常,那是什么导致了这个异常呢?在说这个问题之前我们得先了解一下 Http 的 keep-alive 机制。
可以看到每个 TCP 连接都要经过三次握手建立连接后才能发送数据,要经过四次挥手才能断开连接。
如果每个 TCP 连接在 Server 返回 Response 后都立马断开,则发起多个 Http 请求就要多次创建断开 TCP,这在 Http 请求很多的情况下无疑是很耗性能的。
如果在 Server 返回 Response 不立即断开 TCP 链接,而是复用这条链接进行下一次的 Http 请求,则无形中省略了很多创建/断开 TCP 的开销,性能上无疑会有很大提升。
可以看到发起三次 Http 请求,复用 TCP 的话可以省去两次建立/断开 TCP 的开销。
理论上发起一个应用只要启一个 TCP 连接即可,其他 Http 请求都可以复用这个 TCP 连接,这样 N 次 Http 请求可以省去 N-1 次创建 / 断开 TCP 的开销。这对性能的提升无疑是有巨大的帮助。
回过头来看 keep-alive (又称持久连接,连接复用)做的就是复用连接, 保证连接持久有效。
画中音: Http 1.1 之后 keep-alive 才默认支持并开启,不过目前大部分网站都用了 HTTP 1.1 了,也就是说大部分都默认支持链接复用了。
天下没有免费的午餐 ,虽然 keep-alive 省去了很多不必要的握手/挥手操作,但由于连接长期保活,如果一直没有 Http 请求的话,这条连接也就长期闲着了,会占用系统资源,有时反而会比复用连接带来更大的性能消耗。
所以我们一般会为 keep-alive 设置一个 timeout,这样如果连接在设置的 timeout 时间内一直处于空闲状态(未发生任何数据传输),经过 timeout 时间后,连接就会释放,就能节省系统开销。
看起来给 keep-alive 加 timeout 是完美了,但是又引入了新的问题(一波已平,一波又起)。
考虑如下情况:如果服务端关闭连接,发送 FIN 包(注:在设置的 timeout 时间内服务端如果一直未收到客户端的请求,服务端会主动发起带 FIN 标志的请求以断开连接释放资源)。
在这个 FIN 包发送但是还未到达客户端期间,客户端如果继续复用这个 TCP 连接发送 Http 请求报文的话,服务端会因为在四次挥手期间不接收报文而发送 RST 报文给客户端。
客户端收到 RST 报文就会提示异常(即NoHttpResponseException)。
费了这么大的功夫,我们终于知道了产生 NoHttpResponseException 的原因,那么该怎么解决呢?
有如下两种策略:
重试,收到异常后,重试一两次,由于重试后客户端会用有效的连接去请求,所以可以避免这种情况,不过一次要注意重试次数,避免引起雪崩!
设置一个定时线程,定时清理上述的闲置连接,可以将这个定时时间设置为 keep alive timeout 时间的一半以保证超时前回收。
Makes this instance of HttpClient proactively evict idle connections from the connection pool using a background thread.
解决问题
总结
作者:ErnestEvan,某电商公司 Java 后端工程师
编辑:陶家龙、孙淑娟
出处:转载自微信公众号码海(ID:seaofcode)
精彩文章推荐: