现象
上周线上出了一个问题,现象如下:
- 前端的出现了很多的访问超时
- 后台服务(3个节点)没有任何报错信息
- 后台日志偶尔还是会看到进来的前端请求日志(说明服务端还是会接受请求,并处理请求,就是很慢)
- 后台服务容器内资源的使用情况都处于正常状态
- 由于其他业务产生了大量消费不了的消息循环请求后台服务(3个节点)
- 重启服务(3个节点),开始的时候问题貌似被解决了,前端能够正常接收到后台的返回,但是过了一段儿时间,就用不行了
现象分析
- 通过不停的有大量日志刷入(消费不掉的消息不停的请求后台造成的),怀疑是否因为日志文件过大,导致宿主机磁盘使用率过高?!
进入容器内部,通过 df -f 命令查看,磁盘空间使用率正常 - 通过日志判断有大量的请求需要处理(消费不掉的消息不停的请求后台造成的),故此怀疑是否因为内存使用量过大?!
进入容器内部,通过 top 命令查看,内存使用情况也处于一个比较正常的状态
啊咧?从 1、2 两种分析来看,都脱不开消费不掉的消息不停地请求服务而造成大量的请求的现象。那这个大量的请求对于一个服务来说意味着什么呢?吞吐量?如果超过了服务的吞吐量最大限制会发生什么呢?嗯哼,感觉好像和本次问题出现的现象很接近了呢!!!!继续沿着这个思路我们继续,既然和吞吐量有关系,那么服务的吞吐量是在哪里设置的呢?我们的服务是一个 springboot 项目,默认采用的是 tomcat 作为 web 应用服务器,我们可以通过 application.yml 中的 server.tomcat.max-connections、server.tomcat.max-threads 两个属性来分别指定最大连接数(最多能接受多少个请求)、最大线程数(最多能同时处理多少个请求),如下:
application.yml
server:
tomcat:
# 最大连接数,设置为0为不限制;如果不设置,则默认 10000
max-connections: 1000
# 最大线程数,设置为0不限制;如果不设置,则默认 200
max-threads: 300
# 最小线程数,设置为0不限制;如果不设置,则默认 10
min-spare-threads: 15
# 编码方式
uri-encoding: UTF-8
根据上边配置, 我们知道服务最大能够同时接收 1000 个请求(超过 1000,直接被拒),而这 1000 个请求并不是同时被处理的,其中只有 300 个能被处理,其余的 700 个需要排队等待…是不是有点儿那个味儿了呢?
反向梳理
让我们通过上面的分析再反向梳理下这个问题,首先我们的服务采用的是默认配置(max-connections=10000,max-threads=200),每个节点能够同时处理 200 个请求,3*200=600,也就说目前服务端的处理能力是同时处理 600 个请求,如果超过 600,即使请求能够被服务器接受,那也需要排队等待处理。
咳咳~ 注意啦!下面要对线上的问题进行分析和梳理啦!
由于某业务产生的异常消息无法被消费掉,致使持续重复请求服务(尝试将消息消费掉),造成服务连接资源大量被占用,影响了其他业务的请求,即使其他业务的请求被服务接受,也由于服务的处理能力(只能并发处理 200 个请求,而且其中大部分请求都是异常消息引起的),该请求将会被放到队列中等待处理(排队),当轮到该请求被处理的时候,由于排队+处理时间过长,前端早已超时。
总结
下面通过一张图来做个总结: