一下午连续故障两次,谁把我们接口堵死了?!

打印 上一主题 下一主题

主题 517|帖子 517|积分 1551

唉。。。
大家好,我是程序员鱼皮。又来跟着鱼皮学习线上事故的处置处罚经验了喔!

事故现场

周一下午,我们的 编程导航网站 连续出现了两次故障,每次持续半小时左右,现象是用户无法正常加载网站,一直转圈圈。
用户很快就在群里炸开锅了,甚至有用户表示 “我提进步去了,都不敢刷新。。”

看到这些我真的非常难熬,我们团队的开辟同学也第一时间展开了排查。
简单看了下前端向后端发的请求,发现全部的请求都一直阻塞,直到超时。直接请求后端服务器的接口也是一样的,等了很久都没有正常返回数据。最关键的是,全部接口都阻塞住了,哪怕只是请求个康健检查接口(后端直接返回 "ok",不查询数据库),也无法正常响应。
我们的后端服务是摆设在容器托管平台的,正常环境下如果资源(比如 CPU 和内存)占用超过肯定比例,会自动扩容节点来让服务承载更多的并发请求,但为什么这次没有扩容呢?
其实有经验的朋侪应该已经能猜到接口堵死的原因了,下面我带大家揭开谜团。
 
事故排查

根据上面的现象,推测大概率是接口层出了问题,而不涉及到业务和数据库等依靠资源。由于我们的后端使用的是 Spring Boot + 内嵌的 Tomcat 服务器,而 Tomcat 同时处置处罚请求的最大线程数是固定的(默认是 200),所以当同时处置处罚的请求过多,而且每个请求一直没有处置处罚完成时,全部的线程都在繁忙,没有办法处置处罚新的请求,就会导致新的请求排队等待处置处罚,从而造成了接口堵死(迟迟无法响应)的现象。
这里我用一个简单的程序来模拟下接口堵死和排查过程。
首先写一个非常简单的测试接口,在返回内容前加一个 Thread.sleep,模拟耗时的操作,让处置处罚请求的线程进入较长的等待。

然后更改下 Tomcat 的最大线程数为 5,便于我们模拟线程数不敷的环境:

启动项目,在 Thread.sleep 打断点,然后连续请求 6 次接口。

应该只有 5 次请求会进入断点,末了一次请求会一直转圈卡住,没有线程来处置处罚。这样我们就还原了事故现场。

但以上只是推测,实际线上项目中,怎么去排查确认 Tomcat 线程都阻塞了呢?又怎么确认是哪个接口或代码让 Tomcat 线程阻塞等待了呢?
其实很简单,首先用 jps -l 下令查看 Java 后端服务对应的进程 PID:

然后使用 jstack 下令天生线程快照,并保存为文件。具体下令如下:
  1. jstack <进程PID> > thread_dump.txt
复制代码
打开线程快照文件,全部线程的状态一目了然,搜索 http-nio 就能看到 Tomcat 的请求处置处罚线程,果然全部的请求处置处罚线程状态都是 TIMED_WAITING ,表示线程正在等待另一个线程实行特定的动作,但是有一个指定的等待时间。而且能直接看到请求是阻塞在了哪个代码位置。

利用这个方法,我们也很快定位到了编程导航接口堵死的原因,是发生在一个从数据库查询用户的方法。由于我们昨天下午实行了短信群发召回老用户的动作,导致大量用户同时访问编程导航并实行这个方法。由于涉及的数据库查询操作实行较慢,每个请求都需要等待数据库查询出效果后,才能响应数据,下一个请求才能再进来查询数据库,就导致大量 Tomcat 请求处置处罚线程阻塞在等待数据库查询上,再进一步导致新的请求要排队等待处置处罚。
真相明白了!
 
怎样解决?

其实我们这次碰到的问题就是典型的 “线上连接池爆满问题”,面试的时候也是经常问的。前面讲了怎么排查此类问题,那怎样解决这类问题呢?
首先碰到连接池爆满的环境,先保护现场,比如按照鱼皮上面的操作 dump 线程信息,然后赶紧重启服务或启动新的实例,让用户先能正常使用。再进行排查分析和优化。
怎样优化线上连接池爆满问题?首先肯定还是要优化造成请求阻塞的代码。比如数据库查询慢,我们就通过添加索引来提升查询速度。
还可以增长数据库连接池的巨细,在 Spring Boot 中,默认使用 HikariCP 作为数据源连接池,而 HikariCP 的 maximumPoolSize(最大连接池巨细)默认值只有 10,显然是不足以应对高并发场景的。可以把下面的配置数值调大一点:
  1. spring:<br>datasource:<br>  hikari:<br>    maximum-pool-size: 50
复制代码
由于后端请求操作不止有查询数据库,所以 Tomcat 线程池的最大线程数和最小空闲线程数也可以按需调整,比如下列配置:
  1. # 设置 Tomcat 最大线程数<br>server.tomcat.threads.max=300<br># 设置 Tomcat 最小空闲线程数<br>server.tomcat.threads.min-spare=20
复制代码
适当调大 Tomcat 的最大线程数,可以增长并发请求的处置处罚能力。适当调大 Tomcat 的最小空闲线程数,可以确保在并发高峰时候,Tomcat 能迅速响应新的请求,而不需要重新创建线程。
其实我们大多数环境下,线上服务器(容器)的内存利用率是不高的,所以可以根据实际的资源和并发环境,适本地改一改配置。记得多做做测试,由于过高的线程数可能导致线程调度开销增长,反而降低性能。
 
实际

好吧,以上只是我碰到此类问题的解决方案。但实际可能没那么理想,其实慢 SQL 这个问题我们在上一次故障时就已经定位到,而且在群内同步了。效果你猜怎么着,我们团队的开辟同学发了一堆监控的截图,但是没有一个人真正去解决了这个问题,这才导致了故障在多日之后重新上演!

一旦发现了问题,就必须要想到尽可能长久支持的解决方案,要不然这监控不是白做了?
为什么这次事故持续了这么久呢?也是由于我团队的开辟同学缺少线上问题处置处罚的经验,在那一通分析,效果忘了规复服务,过了半个小时用户还是无法访问,直到我去提醒。。。

所以这个时候就知道平常背的八股文有多紧张了吧?Tomcat 的连接器配置和性能优化也是一道经典的八股文,也是我们 面试鸭刷题神器 收录的题目。这些知识等到真出了线上问题时,都是用的上的。

吃一堑,长一智,颠末这次的事故,我信赖团队的同学又一次成长了。读者朋侪们,你们有劳绩没有嘞~
 
更多



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表