前言

到了年底果然都不太平,最近又收到了运维报警:表示有些服务器负载非常高,让我们定位问题。

还真是想什么来什么,前些天还故意把某些服务器的负载提高(没错,老板让我写个 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 也发生内存溢出?

没想到又来一出。

为了更加直观的查看线程的状态信息,我将快照信息上传到专门分析的平台上。

http://fastthread.io/

其中有一项菜单展示了所有消耗 CPU 的线程,我仔细看了下发现几乎都是和上面的堆栈一样。

也就是说都是 Disruptor 队列的堆栈,同时都在执行 java.lang.Thread.yield 函数。

众所周知 yield 函数会让当前线程让出 CPU 资源,再让其他线程来竞争。

根据刚才的线程快照发现处于 RUNNABLE 状态并且都在执行 yield 函数的线程大概有 30几个。

因此初步判断为大量线程执行 yield 函数之后互相竞争导致 CPU 使用率增高,而通过对堆栈发现是和使用 Disruptor 有关。

解决问题

而后我查看了代码,发现是根据每一个业务场景在内部都会使用 2 个 Disruptor 队列来解耦。

假设现在有 7 个业务类型,那就等于是创建 2*7=14Disruptor 队列,同时每个队列有一个消费者,也就是总共有 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% 排查优化实践的更多相关文章

  1. Java死锁排查和Java CPU 100% 排查的步骤整理

    ================================================= 人工智能教程.零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助! 点击查看高清无码教程 == ...

  2. [转]Java CPU 100% 排查技巧

    文章来源:微信公众号:猿天地 平时多积累一点,这样在遇到问题的时候就少句求人的话.如果在实际的开发中遇到CPU 100%问题,要怎么排查呢?如果你没有遇到过这个问题,请先自己思考10s,如果你遇到过, ...

  3. 阿里短信回持.net sdk的bug导致生产服务cpu 100%排查

    一:背景 1. 讲故事 去年阿里聚石塔上的所有isv短信通道全部对接阿里通信,我们就做了对接改造,使用阿里提供的.net sdk. 网址:https://help.aliyun.com/documen ...

  4. mysql cpu 100% 满 优化方案

    解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/details/5630782 ...

  5. mysql cpu 100% 满 优化方案 解决MySQL CPU占用100%的经验总结

    下面是一些经验 供参考 解决MySQL CPU占用100%的经验总结 - karl_han的专栏 - CSDN博客 https://blog.csdn.net/karl_han/article/det ...

  6. java CPU 100% 排查(转载)

    一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. (友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hank ...

  7. java CPU 100% 排查

    一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. (友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hank ...

  8. 再一次生产 CPU 高负载排查实践

    前言 前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨. 其实早在去年我也处理过类似的问题,并记录下来:<一次生产 CPU 1 ...

  9. Linux(2)---记录一次线上服务 CPU 100%的排查过程

    Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...

随机推荐

  1. Java 读书笔记 (九) 运算符

    短路逻辑运算符 && 当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了. ...

  2. Python 魔术方法笔记

    魔术方法总是被__包围, 如__init__ , __len__都是常见的魔术方法,这里主要写一下我遇到的一些魔术方法 setitem 对某个索引值赋值时 即可以进行赋值操作,如 def __seti ...

  3. jsp 基础知识之指令元素

    由于考研和结业的事情,这里荒废了许久,而如今重新捡起来,是因为带到公司的碳素笔没有油了......    jsp的指令元素:通常以<%@开始,以%>结尾. jsp主要包括三种指令元素:pa ...

  4. MYSQL—— year类型的使用与注意点!

    mysql的日期与时间类型:分为time.date.datetime.timestamp.year,主要总结下year的用法: 1.类型支持:year 与 year(4),注意无year(2)的定义方 ...

  5. selenium+python+eclipse 实现 “问卷星”网站,登录与检查登录示例!

    1.使用selenium+python+eclipse实现的登录"问卷星",问卷星访问地址:https://www.sojump.com/ 2.实现步骤:1)进入链接---首页-- ...

  6. SAP S4系统创建Customer和Vendor的BAPI

    对应的BAPI是:RFC_CVI_EI_INBOUND_MAIN SAP 又调皮了,又不安常理出牌!

  7. JDK--box和unbox

    目录 什么是装箱.拆箱 基本类型和包装类型 为什么会有基本类型? 为什么还要有包装类型 两者区别 两者互转 源码分析(JDK1.8版本) valueOf方法 1.Integer.Short.Byte. ...

  8. ASP.NET Core 实现带认证功能的Web代理服务器

    引言 最近在公司开发了一个项目,项目部署架构图如下: 思路 如图中文本所述,公司大数据集群不允许直接访问外网,需要一个网关服务器代理请求,本处服务器A就是边缘代理服务器的作用. 通常技术人员最快捷的思 ...

  9. 微服务架构 - SpringBoot整合Jooq和Flyway

    在一次学习分布式跟踪系统zipkin中,发现了jooq这个组件,当时不知这个组件是干嘛的,后来抽空学习了一下,感觉这个组件还挺用的.它主要有以下作用: 通过DSL(Domain Specific La ...

  10. 女皇武则天:我不愿被 extends

    01. 利用继承,我们可以基于已存在的类构造一个新类.继承的好处在于,子类可以复用父类的非 private 的方法和非 private 成员变量. is-a 是继承的一个明显特征,就是说子类的对象引用 ...