一次生产 CPU 100% 排查优化实践

前言
到了年底果然都不太平,最近又收到了运维报警:表示有些服务器负载非常高,让我们定位问题。
还真是想什么来什么,前些天还故意把某些服务器的负载提高(没错,老板让我写个 BUG!),不过还好是不同的环境互相没有影响。
定位问题
拿到问题后首先去服务器上看了看,发现运行的只有我们的 Java 应用。于是先用 ps 命令拿到了应用的 PID。
接着使用 top -Hp pid 将这个进程的线程显示出来。输入大写的 P 可以将线程按照 CPU 使用比例排序,于是得到以下结果。

果然某些线程的 CPU 使用率非常高。
为了方便定位问题我立马使用 jstack pid > pid.log 将线程栈 dump 到日志文件中。
我在上面 100% 的线程中随机选了一个 pid=194283 转换为 16 进制(2f6eb)后在线程快照中查询:
因为线程快照中线程 ID 都是16进制存放。

发现这是 Disruptor 的一个堆栈,前段时间正好解决过一个由于 Disruptor 队列引起的一次 OOM:强如 Disruptor 也发生内存溢出?
没想到又来一出。
为了更加直观的查看线程的状态信息,我将快照信息上传到专门分析的平台上。

其中有一项菜单展示了所有消耗 CPU 的线程,我仔细看了下发现几乎都是和上面的堆栈一样。
也就是说都是 Disruptor 队列的堆栈,同时都在执行 java.lang.Thread.yield 函数。
众所周知 yield 函数会让当前线程让出 CPU 资源,再让其他线程来竞争。
根据刚才的线程快照发现处于 RUNNABLE 状态并且都在执行 yield 函数的线程大概有 30几个。
因此初步判断为大量线程执行 yield 函数之后互相竞争导致 CPU 使用率增高,而通过对堆栈发现是和使用 Disruptor 有关。
解决问题
而后我查看了代码,发现是根据每一个业务场景在内部都会使用 2 个 Disruptor 队列来解耦。
假设现在有 7 个业务类型,那就等于是创建 2*7=14 个 Disruptor 队列,同时每个队列有一个消费者,也就是总共有 14 个消费者(生产环境更多)。
同时发现配置的消费等待策略为 YieldingWaitStrategy 这种等待策略确实会执行 yield 来让出 CPU。
代码如下:

初步看来和这个等待策略有很大的关系。
本地模拟
为了验证,我在本地创建了 15 个 Disruptor 队列同时结合监控观察 CPU 的使用情况。


创建了 15 个 Disruptor 队列,同时每个队列都用线程池来往 Disruptor队列 里面发送 100W 条数据。
消费程序仅仅只是打印一下。

跑了一段时间发现 CPU 使用率确实很高。

同时 dump 线程发现和生产的现象也是一致的:消费线程都处于 RUNNABLE 状态,同时都在执行 yield。
通过查询 Disruptor 官方文档发现:

YieldingWaitStrategy 是一种充分压榨 CPU 的策略,使用
自旋 + yield的方式来提高性能。
当消费线程(Event Handler threads)的数量小于 CPU 核心数时推荐使用该策略。

同时查阅到其他的等待策略 BlockingWaitStrategy (也是默认的策略),它使用的是锁的机制,对 CPU 的使用率不高。
于是在和之前同样的条件下将等待策略换为 BlockingWaitStrategy。



和刚才的 CPU 对比会发现到后面使用率的会有明显的降低;同时 dump 线程后会发现大部分线程都处于 waiting 状态。
优化解决
看样子将等待策略换为 BlockingWaitStrategy 可以减缓 CPU 的使用,
但留意到官方对 YieldingWaitStrategy 的描述里谈道:
当消费线程(Event Handler threads)的数量小于 CPU 核心数时推荐使用该策略。
而现有的使用场景很明显消费线程数已经大大的超过了核心 CPU 数了,因为我的使用方式是一个 Disruptor 队列一个消费者,所以我将队列调整为只有 1 个再试试(策略依然是 YieldingWaitStrategy)。


跑了一分钟,发现 CPU 的使用率一直都比较平稳而且不高。
总结
所以排查到此可以有一个结论了,想要根本解决这个问题需要将我们现有的业务拆分;现在是一个应用里同时处理了 N 个业务,每个业务都会使用好几个 Disruptor 队列。
由于是在一台服务器上运行,所以 CPU 资源都是共享的,这就会导致 CPU 的使用率居高不下。
所以我们的调整方式如下:
- 为了快速缓解这个问题,先将等待策略换为
BlockingWaitStrategy,可以有效降低 CPU 的使用率(业务上也还能接受)。 - 第二步就需要将应用拆分(上文模拟的一个
Disruptor队列),一个应用处理一种业务类型;然后分别单独部署,这样也可以互相隔离互不影响。
当然还有其他的一些优化,因为这也是一个老系统了,这次 dump 线程居然发现创建了 800+ 的线程。
创建线程池的方式也是核心线程数、最大线程数是一样的,导致一些空闲的线程也得不到回收;这样会有很多无意义的资源消耗。
所以也会结合业务将创建线程池的方式调整一下,将线程数降下来,尽量的物尽其用。
本文的演示代码已上传至 GitHub:
https://github.com/crossoverJie/JCSprout
你的点赞与分享是对我最大的支持
一次生产 CPU 100% 排查优化实践的更多相关文章
- Java死锁排查和Java CPU 100% 排查的步骤整理
================================================= 人工智能教程.零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助! 点击查看高清无码教程 == ...
- [转]Java CPU 100% 排查技巧
文章来源:微信公众号:猿天地 平时多积累一点,这样在遇到问题的时候就少句求人的话.如果在实际的开发中遇到CPU 100%问题,要怎么排查呢?如果你没有遇到过这个问题,请先自己思考10s,如果你遇到过, ...
- 阿里短信回持.net sdk的bug导致生产服务cpu 100%排查
一:背景 1. 讲故事 去年阿里聚石塔上的所有isv短信通道全部对接阿里通信,我们就做了对接改造,使用阿里提供的.net sdk. 网址:https://help.aliyun.com/documen ...
- mysql cpu 100% 满 优化方案
解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/details/5630782 ...
- mysql cpu 100% 满 优化方案 解决MySQL CPU占用100%的经验总结
下面是一些经验 供参考 解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/det ...
- java CPU 100% 排查(转载)
一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. (友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hank ...
- java CPU 100% 排查
一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. (友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hank ...
- 再一次生产 CPU 高负载排查实践
前言 前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨. 其实早在去年我也处理过类似的问题,并记录下来:<一次生产 CPU 1 ...
- Linux(2)---记录一次线上服务 CPU 100%的排查过程
Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...
随机推荐
- Effective Java 第三版——43.方法引用优于lambda表达式
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Feature Preprocessing on Kaggle
刚入手data science, 想着自己玩一玩kaggle,玩了新手Titanic和House Price的 项目, 觉得基本的baseline还是可以写出来,但是具体到一些细节,以至于到能拿到的出 ...
- Java I/O流详解与应用
注:学习之前,需要自己思考,不要盲目的去看!!!!!不然没有任何意义 I/O流(一) 1.定义:实现设备之间的传输 2.作用:实现数据的读入和写出 3.分类:根据操作分为:输入流和输出流 根据类型分为 ...
- 深入浅出Git教程(转载)
目录 一.版本控制概要 1.1.什么是版本控制 1.2.常用术语 1.3.常见的版本控制器 1.4.版本控制分类 1.4.1.本地版本控制 1.4.2.集中版本控制 1.4.3.分布式版本控制 1.5 ...
- hystrix隔离策略(4)
hystrix提供了两种隔离策略:线程池隔离和信号量隔离.hystrix默认采用线程池隔离. 1.线程池隔离 不同服务通过使用不同线程池,彼此间将不受影响,达到隔离效果. 例如: 我们可以通过andT ...
- UML用法及状态图,活动图介绍
统一建模语言UML(Unified Modeling Language)是非专利的第三代建模和规约语言.UML是一种开放的方法,用于说明.可视化.构建和编写一个正在开发的.面向对象的.软件密集系统的制 ...
- 『Zap Möbius反演』
Zap Description FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a ,y<=b,并且gcd(x,y)=d.作为F ...
- 干货|一个案例学会Spring Security 中使用 JWT
在前后端分离的项目中,登录策略也有不少,不过 JWT 算是目前比较流行的一种解决方案了,本文就和大家来分享一下如何将 Spring Security 和 JWT 结合在一起使用,进而实现前后端分离时的 ...
- Java并发编程面试题 Top 50 整理版
本文在 Java线程面试题 Top 50的基础上,对部分答案进行进行了整理和补充,问题答案主要来自<Java编程思想(第四版)>,<Java并发编程实战>和一些优秀的博客,当然 ...
- Spring Boot整合Mybatis并完成CRUD操作
MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官 ...