生产环境CPU过高如何排查?
这里我先 写一个小的Demo, 开启两个线程,其中一个死循环,占满CPU
运行:
可以看到此时CPU明显提升了很多,单核已经满了
我们切换到tmux的另一个会话:
使用top
命令查看CPU占用情况
可以看到有一个进程1335655
已经占用100了,我们知道进程里面运行着的是线程,因此我们要去看线程的占用情况
ps H
查看对应的线程信息,-eo
输出指定的一些信息
ps h - ep pid ,tid, %cpu | grep pid 查指定进程中线程所占CPU
这里我输入:
ps H -eo pid,tid,%cpu |grep 1335655
也就是我们看到了这个进程对应的线程占用情况,可以看到是1335674
占用达到了100
下面我们就要看这个线程的情况
jstack 1335655
注意后面是pid,而不是刚刚的线程id
可以看到每个线程都有一个tid和nid,并且nid都是16进制,那我们现在要做的就是去找刚刚CPU占用高的线程对应的16进制是什么
16进制查看可以使用
printf %x 数字
1335674对应的16进制是14617a,如图所示:
可以看到代码是在第4行有问题,我们再回头看源码,第四行对应的正是我们while死循环的地方,而一个while循环就把我们的单核CPU跑满了。
网关排查
https://blog.csdn.net/banzizong2551/article/details/102042347
https://developer.jboss.org/thread/274758
https://blog.csdn.net/m0_37055174/article/details/115435867
网关项目:
进入Docker容器查看:
ps H -eo pid,tid,%cpu |grep 7
找到对应的线程:
7 65 87.7
7 66 22.7
获取65对应的16进制printf %x 65
查看找到位置的后几行:grep -A n
n表示后几行
jstack 7 |grep 0x41 -A 10
"XNIO-1 I/O-3" #56 prio=5 os_prio=0 tid=0x00007f5de17e1000 nid=0x41 runnable [0x00007f5d4f9fa000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x00000000c1d0ab68> (a sun.nio.ch.Util$3)
- locked <0x00000000c1d0ab58> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000c1d0aa40> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
at org.xnio.nio.WorkerThread.run(WorkerThread.java:532)
保存所有信息:jstack 7 > 7cpu.log
看有多少RUNNABLE线程:
[root@dev141 logs]# grep RUNNABLE 7cpu.log |wc -l
19
找不到问题,使用https://arthas.aliyun.com/doc/quick-start.html#_2-%E5%90%AF%E5%8A%A8-arthas
https://github.com/spring-cloud/spring-cloud-gateway/issues/1908
排除掉:
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</exclusion>
修改webclient去掉mono.block
@Override
public BaseResult<VerifyTokenResult<UserInfo>> verifyUserToken(String userToken) {
Mono<BaseResult> mono = webClient.get()
.uri(uriBuilder -> uriBuilder.path("/auth/user/verifyUserToken")
.queryParam("userToken", userToken).build())
.retrieve()
.bodyToMono(BaseResult.class);
return (BaseResult<VerifyTokenResult<UserInfo>>) mono.block();
}
修改为:
public Mono<BaseResult<VerifyTokenResult<UserInfo>>> verifyUserToken(String userToken) {
return webClient.get()
.uri(uriBuilder -> uriBuilder.path("/auth/user/verifyUserToken")
.queryParam("userToken", userToken).build())
.retrieve()
.bodyToMono(new ParameterizedTypeReference<BaseResult<VerifyTokenResult<UserInfo>>>() {});
}