项目崩溃,Web页面进不去,用top命令查看发现java和mysql占用的CPU异常高,服务器配置的16G内存,也只剩几百M了。
排查过程:
- 查看日志,各应用日志没有发现明显的异常,但是在Tomcat的日志中却疯狂出现OutOfMemoryError异常。
- 抓取dump文件进行分析,发现一个接收mq的处理类里面用了ArrayBlockingQueue,每一次mq消息过来之后,并不是马上就处理,而是先添加到BlockingQueue里面,之后再从BlockingQueue中取出消息进行处理。问题也就出现在这个Queue里面,当时设的这个queue的最大值为100000。出问题时,队列里还有十多万的数据堆积,也就是说queue缓存队列已经塞满了,查看dump文件,也确实这个BlockingQueue就占用了600多m的内存,而且不仅这一个队列,还有另外一个缓存队列也占用了近300M的内存大小。
- 发现这个问题之后,为了让现场可以继续使用,马上将BlockingQueue的大小改小了,重新打包扔到线上。
- 出现这个问题的原因也有因为消费者是单线程消费而且每次只从BlockingQueue中poll出一条数据进行处理的原因,后续改成了多线程消费,且每次从队列中取出20条数据进行处理。这样Java的内存消耗和CPU占用也就降下来了。
- 但是MySQL的占用依然很高,使用mysql -h 127.0.0.1 -u mysql -p 登录到数据库之后,使用show full processlist;发现有一条SQL的执行效率比较低,需要2S多的时间,而且这条SQL还在大量的使用。
- 查看对应的表发现这个表没有建索引,根据条件字段建立索引之后,MySQL的CPU占用率立马降下来了。这样,线上的应用也就恢复了正常
- 这样跑了两天之后,突然又崩溃了。但是这次Java和MySQL的内存和CPU占用都不高,这就很奇怪了。
- 刚开始查看了日志,发现RabbitMq连接不上,日志中一直在报RabbitMq Connect time out的错误。但是大佬说这应该不会影响整个应用,导致web页面进不去(页面报read time out异常),于是就就继续找原因。开始抓取线上的堆栈信息进行分析。
- 之后,还发现一个现象,所有通过http方式访问Tomcat中的应用的地方都不行了。
- 最后发现,应用对外提供了一个http接口,在这个接口中会发送RabbitMq消息给其他系统,而这时的RabbitMq是连不上的,但是,每一次请求过来都会去新建一个Cononection和Channel,这样就会导致在调用这个接口的时候需要等到RabbitMq连接超时了之后,才会断开。而RabbitMq的超时时间默认是1分钟,而在这一分钟内,这个接口被大量的调用,导致大量的http连接资源被占用得不到及时的释放。
- 查看Tomcat Server.xml中的配置,了解到配置的MaxThreads为150,MaxAccept为300。也就是说最多支持的http并发连接为450
- 在项目中,由于设备采集的人脸、车辆抓拍的信息相当多,再加上RabbitMq连接不上,也就出现了这个问题。
- 最后,当然是配置了正确的RabbitMq地址之后,解决了问题。