由http超时引发的故障分析

起因

通过日志观察到任务执行一段时间后停止工作

分析过程

先介绍一下业务逻辑,任务通过一个接口触发,会在一个单线程的线程池中开启一个任务,任务逻辑为不断从数据库中查询出数据并将数据放入一个阻塞队列中等待消费者消费。消费者会单线程循环不断从队列中获取消息,然后放入线程池之中执行。消费者线程池中线程执行的逻辑为发送http请求,通过响应的结果回写数据。

ok,整个业务逻辑很简单,让我们开始排查故障吧。

1、是不是所有任务都执行完毕因此任务停止?

检查数据库之后发现仍有未执行任务,因此排除此问题

2、是不是消费端发生了死循环?

我查看了服务器状态,发现cpu使用率很低,因此排除是死循环导致
在这里插入图片描述

3、是不是数据库发生死锁导致生产者无法往队列中插入数据,导致任务停止?

在这里插入图片描述
结果一切正常,因此排除数据库死锁问题

4、是不是发生频繁gc导致应用异常?

这里图忘记保存了

查看之后发现gc很正常,没有fullgc,因此排除gc导致的问题


到这里我发现我的日志不够,于是补充大量日志之后开始第二轮排查

观察日志过程中发现,消费者端线程池中的线程会渐渐停止工作

6、是不是发生了死锁???

我们用jstack看下线程状态
在这里插入图片描述
观察发现,消费者线程都处于runnable状态,并未发生阻塞

ps.划重点。现在回想起来,这里严重误导了我的判断

7、让我们来看下内存吧

在这里插入图片描述
dump之后发现内存一切正常,从内存内容中能看出来消费者线程池中的线程并未死亡。

这里我就又开始了陷入了自我怀疑之中,是不是逻辑哪里出了问题,于是我开始了反复的自我折磨,反复的进行各种设想。


经过一段漫长的折腾,我突然想到,任务中既然有Http请求那么我看下链接状态吧
在这里插入图片描述
这么一看,发现问题了,目前所有消费者线程都处于停止状态,但链接都是ESTABLISHED,这肯定有问题

这时已经有思路了:应该是因为线程在建立连接之后,一直未收到数据,但也没有断开链接,导致线程阻塞,这种情况一般是由于没有设置超时时间导致的

于是开始查看restTemplate源码,发现默认超时时间都是0,代表不超时一直等待
在这里插入图片描述
这就好办了,我们只需要设置好超时时间就ok了
在这里插入图片描述
设置后发现任务执行正常,问题得到解决


回顾

此次故障问题算不上复杂,但由于问题的复现需要依赖网络偶然的问题,需要的时间较长,因此排查起来非常难受。

现在想来
1、我犯了一个想当然的错误,那就是线程是runnable状态代表线程未发生阻塞.

这其实是一个惯性思维,因为平时遇到的阻塞情况线程要么处于waitting状态,要么处于blocking状态,因此看到runnable就觉得没问题,因此花费了大量的排查时间。
2、应该多加一些监控的日志,方便排查问题
3、是要熟悉各种组件,了解其核心之后再进行使用,并在使用时根据具体情况定制核心参数

猜你喜欢

转载自blog.csdn.net/KevinDai007/article/details/108398982