点击上方 好好学java ,选择 星标 公众号重磅资讯、干货,第一时间送达
今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W+访问量博客:点击前往,查看更多

案发现场

昨天晚上突然短信收到 APM (即 Application Performance Management 的简称,我们内部自己搭建了这样一套系统来对应用的性能、可靠性进行线上的监控和预警的一种机制)大量告警

画外音: 监控是一种非常重要的发现问题的手段,没有的话一定要及时建立哦

紧接着运维打来电话告知线上部署的四台机器全部 OOM (out of memory, 内存不足),服务全部不可用,赶紧查看问题!

问题排查

首先运维先重启了机器,保证线上服务可用,然后再仔细地看了下线上的日志,确实是因为 OOM 导致服务不可用

第一时间想到 dump 当时的内存状态,但由于为了让线上尽快恢复服务,运维重启了机器,导致无法 dump 出事发时的内存。所以我又看了下我们 APM 中对 JVM 的监控图表

画外音:一种方式不行,尝试另外的角度切入!再次强调,监控非常重要!完善的监控能还原当时的事发现场,方便定位问题。

不看不知道,一看吓一跳,从 16:00 开始应用中创建的线程居然每时每刻都在上升,一直到 3w 左右,重启后(蓝色箭头),线程也一直在不断增长),正常情况下的线程数是多少呢,600!

问题找到了,应该是在下午 16:00 左右发了一段有问题的代码,导致线程一直在创建,且创建的线程一直未消亡!

查看发布记录,发现发布记录只有这么一段可疑的代码 diff:在 HttpClient 初始化的时候额外加了一个 evictExpiredConnections 配置

问题定位了,应该是就是这个配置导致的!(线程上升的时间点和发布时间点完全吻合!),于是先把这个新加的配置给干掉上线,上线之后线程数果然恢复正常了。

那 evictExpiredConnections 做了什么导致线程数每时每刻在上升呢?这个配置又是为了解决什么问题而加上的呢?

于是找到了相关同事来了解加这个配置的前因后果

还原事发经过

最近线上出现不少 NoHttpResponseException 的异常, 而上面那个配置就是为了解决这个异常而添加的,那是什么导致了这个异常呢?

在说这个问题之前我们得先了解一下 http 的 keep-alive 机制。

先看下正常的一个 TCP 连接的生命周期

可以看到每个 TCP 连接都要经过三次握手建立连接后才能发送数据,要经过四次挥手才能断开连接

如果每个 TCP 连接在 server 返回 response 后都立马断开,则发起多个 HTTP 请求就要多次创建断开 TCP, 这在 Http 请求很多的情况下无疑是很耗性能的

如果在 server 返回 response 不立即断开 TCP 链接,而是复用这条链接进行下一次的 Http 请求,则无形中省略了很多创建 / 断开 TCP 的开销,性能上无疑会有很大提升。

如下图示,左图是不复用 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 的原因,这样能看得更明白一些

费了这么大的功夫,我们终于知道了产生 NoHttpResponseException 的原因,那该怎么解决呢

有两种策略:

  1. 重试,收到异常后,重试一两次,由于重试后客户端会用有效的连接去请求,所以可以避免这种情况,不过一次要注意重试次数,避免引起雪崩!

  2. 设置一个定时线程,定时清理上述的闲置连接,可以将这个定时时间设置为 keep alive timeout 时间的一半以保证超时前回收。

evictExpiredConnections 就是用的上述第二种策略,来看下官方用法使用说明

Makes this instance of HttpClient proactively evict idle connections from the
connection pool using a background thread.

调用这个方法只会产生一个定时线程,那为啥应用中线程会一直增加呢,因为我们对每一个请求都创建了一个 HttpClient! 这样由于创建每一个 HttpClient 实例j时都会调用  evictExpiredConnections ,导致有多少请求就会创建多少个定时线程!

还有一个问题,为啥线上四台机器几乎同一时间点全挂呢?

因为由于负载均衡,这四台机器的权重是一样的,硬件配置也一样,收到的请求其实也可以认为是差不多的,这样这四台机器由于创建 HttpClient 而生成的后台线程也在同一时间达到最高点,然后同时 OOM。

解决问题

所以针对以上提到的问题,我们首先把 HttpClient 改成了单例,这样保证服务启动后只会有一个定时清理线程

另外我们也让运维针对应用的线程数做了监控,如果超过某个阈值直接告警,这样能在应用 OOM 前及时发现处理。

画外音:再次强调,监控相当重要,能把问题扼杀在摇篮里!

总结

本文通过线上四台机器同时 OOM 的现象,来详细剖析定位了产生问题的原因,可以看到我们在应用某个库时首先要对这个库要有充分的了解(上述 HttpClient 的创建不用单例显然是个问题)

其次必要的网络知识还是需要的,所以要成为一个合格的程序员,不光对语言本身有所了解,还要对网络,数据库等也要有所涉猎,这些对排查问题以及性能调优等会有非常大的帮助

再次,完善的监控非常重要,通过触发某个阈值提前告警,可以将问题扼杀在摇篮里!

推荐文章
  • 今天给大家推荐 6 个 Spring Boot 项目,拿来就可以赚钱!

  • 分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!

  • 圈子哥推荐一款基于 Spring Boot 开发 OA 开源产品,学习/搞外快都是不二选择!

  • 硬刚一周,3W字总结,一年的经验告诉你如何准备校招!

原创电子书历时整整一年总结的 Java 面试 + Java 后端技术学习指南,这是本人这几年及校招的总结,各种高频面试题已经全部进行总结,按照章节复习即可,已经拿到了大厂offer。
原创思维导图扫码或者微信搜 程序员的技术圈子 回复 面试 领取原创电子书和思维导图。

线上 4 台机器同一时间全部 OOM,到底发生了什么?相关推荐

  1. 震惊!线上四台机器同一时间全部 OOM,到底发生了什么?

    来自:码海 案发现场 昨天晚上突然短信收到 APM (即 Application Performance Management 的简称,我们内部自己搭建了这样一套系统来对应用的性能.可靠性进行线上的监 ...

  2. 接收不到其他机器发来的报文_大厂真实案例:线上四台机器同一时间全部 OOM......

    # 案发现场 昨天晚上突然短信收到 APM (即 Application Performance Management 的简称,我们内部自己搭建了这样一套系统来对应用的性能.可靠性进行线上的监控和预警 ...

  3. 线上四台机器同一时间全部 OOM,到底发生了什么?

    作者 | 码海 责编 | Elle 案发现场 昨天晚上突然短信收到 APM (即 Application Performance Management 的简称,我们内部自己搭建了这样一套系统来对应用的 ...

  4. mobsdk线上崩溃事故报告_重大事故!IO问题引发线上20台机器同时崩溃

    几年前的一个下午,公司里码农们正在安静地敲着代码,突然很多人的手机同时"哔哔"地响了起来.本来以为发工资了,都挺高兴!打开一看,原来是告警短信 故障回顾 告警提示"线程数 ...

  5. mysql爆内存_线上MySQL数据库机器内存爆掉原因分析与解决

    本文主要向大家介绍了线上MySQL数据库机器内存爆掉原因分析与解决,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 现象: 阿里金融某业务的MySQL机器的内存每隔几天就会增长,涨 ...

  6. linux同步多台机器的时间

    除了用NTP服务器之外,下面的命令可以做到让几台机器的时间都跟一台机器同步. date --set="$(ssh root@192.168.10.101 date)"

  7. 【线上分享】机器视觉编码标准与技术进展

    5月6日 19:30,我们邀请到了 中国电信研究院新技术所 机器视觉标准与应用研究部主任 张园 详细介绍VCM.DCM等标准组织机器视觉编码标准化工作最新进展.技术创新思路. 一同探讨面向机器视觉的视 ...

  8. 记录一次线上CPU负载过高的排查过程

    背景 一大早收到运维同学反馈.线上某台机器cpu的负载达到了97%以上,为了不影响机器上服务的正常运行,急需找到导致负载过高的原因并将负载降到合理的区间. 用到命令 top/uptime:查看负载情况 ...

  9. 如何同步两台Linux机器的时间?

    除了用NTP服务器之外,下面的命令可以做到让几台机器的时间都跟一台机器同步. date --set="$(ssh root@10.245.110.101 date)" 参考资料 = ...

最新文章

  1. 大道至简阅读笔记02
  2. sqlserver怎么将excel表的数据导入到数据库中
  3. 关于Postfix邮件网关无法重启问题
  4. 2410Init.s
  5. 面试官问:上亿数据量下,Kafka是如何优化JVM GC问题的?
  6. dict格式转字符串两种方法的区别
  7. Xor Transformation
  8. Python中的logging模块
  9. 如何在vim中交换两个打开文件(在拆分中)的位置?
  10. 密码学09(SM3算法)
  11. android netd的工作流程解析
  12. 怎样批量缩小图片大小kb?图片怎么批量压缩?
  13. Adobe Flash Debugger 下载
  14. 记win10 ltsc版本wsappx进程CPU占用过高解决过程
  15. CNN原理及模型参数
  16. Linux qt教程 qt入门(一)
  17. redis多实例应用
  18. 【音乐编程】基础知识一
  19. 7-1 sdut-oop-7 答答租车系统(类的继承与多态 面向对象综合练习) (30 分)
  20. python全栈是什么?

热门文章

  1. MySql 长时间读数据发生超时的异常 Mysql Reader Exception TimeOut expired
  2. HDOJ 1071 The area (纯数学题)
  3. 使用FLVPlayback组件播放fms(fcs)的流式FLV文件
  4. AB1601继电器干扰问题
  5. 关于点击率模型,你知道这三点就够
  6. 第六章-template模板
  7. Python:win32com 模块
  8. STL源代码分析(ch2 内存分配)uninitialized_fill_n
  9. 【漫天烟花】绚烂烟花点亮夜空也太美了叭、某程序员携带烟花秀给大家拜年啦~
  10. Linux Kernel 5.14 arm64异常向量表解读-中断处理解读