记一次线上CPU100%的问题排查记录
某一天中午,线上突然收到告警,提示某一个服务的部分集群 CPU 使用率长时间超出100%,
由于我们是三集群部署,假设三个集群分别是A、B、C,其中 ABC 都可以被下游服务 S1 调用,而 B 集群是由于是就近访问,只能被 S2 服务调用,问题恰好出现在 B 集群。
该服务是一个典型的IO密集型应用,平时的CPU基本处于10%以下
查看服务的发版记录,发现最近一次发版记录是一天前,所以初步排除是最近的需求导致的。
太长不看版,直接说原因
用 Java 进行解压缩流的时候,有一个空循环,在流的数据出现损坏的时候,会出现空循环。
开始排查
排查思路
1)请求量太大?
由于是一天前发版本后再也没有进行变更,所以首先猜测是突然增加的请求量导致的,查看后请求量十分的平稳,无明显波动。
于是决定先恢复业务,重新按批次发布B集群,但是在新发布的几台机器上,CPU也迅速被打满了,这个时候情况就变的很紧急了。
2)Redis中含有大Key?
此时另一个小伙伴说Redis连接有异常,部分超时,但是 Redis 连接异常是导致CPU被打满的原因吗?不是。
首先项目中是使用连接池在管理 Redis 的连接,所以不会出现无限创建连接的情况,其次如果是Redis导致的CPU打满,那么会是什么原因呢?
大Key:序列化、反序列化
大Key 的序列化和反序列化,虽然会导致 CPU 使用率上升,但是也一定会导致 GC 的上升。但是那么服务当时GC非常正常,所以暂时可以排出这个原因。
但是 CPU 被打满以后,是可以导致Redis连接出现异常的
现代 CPU 的调度是分时调度,如果某一个线程已运行时间太长,是会减少其他线程的使用时间的,而恰好如果是 Redis 的线程时间被挤占了,超出了socketTimeOut时间,那么就会出现超时。
3)线程Dump
于是查看线程,发现有几个线程占用 CPU时间非常长,而且代码一直在读取流,

查看出现死循环的代码如下:
1 | while (!inflater.finished()) { |
解决方案也很简单:
1 | while (!inflater.finished()) { |
解决方案参考:java-inflater-will-loop-infinitely-sometimes
最后
遇到 CPU 打满的问题,可以先看看请求量,如果没有请求量的突增,基本就是代码出现了死循环
记一次线上CPU100%的问题排查记录
https://somersames.github.io/2024/04/04/记一次线上CPU100-的问题排查记录/