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

简介: 收到了运维报警:表示有些服务器负载非常高,让我们定位问题。

定位问题


拿到问题后首先去服务器上看了看,发现运行的只有我们的 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 也发生内存溢出?


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


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:


github.com/crossoverJi…


相关文章
|
2月前
|
存储 弹性计算 安全
阿里云轻量服务器通用型、CPU优化型、多公网IP型、国际型、容量型不同实例区别与选择参考
阿里云轻量应用服务器实例类型分为通用型、CPU优化型、多公网IP型、国际型、容量型,不同规格族的适用场景和特点不同,收费标准也不一样。本文为大家介绍轻量应用服务器通用型、多公网IP型、容量型有何区别?以及选择参考。
|
19天前
|
存储 缓存 数据挖掘
阿里云轻量应用服务器“CPU优化型”配置介绍、费用价格说明
阿里云轻量应用服务器推出CPU优化型,提供更强计算性能,2核4GB起,最高16核64GB,全系支持200Mbps带宽。适用于企业级应用、数据库、游戏服务器等高算力场景,保障稳定高效运行。
164 1
|
2月前
|
缓存 关系型数据库 MySQL
降低MySQL高CPU使用率的优化策略。
通过上述方法不断地迭代改进,在实际操作中需要根据具体场景做出相对合理判断。每一步改进都需谨慎评估其变动可能导致其他方面问题,在做任何变动前建议先在测试环境验证其效果后再部署到生产环境中去。
110 6
|
9月前
|
存储 设计模式 监控
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
933 166
|
7月前
|
存储 设计模式 监控
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
176 0
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
|
9月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
216 13
|
10月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
940 7
|
12月前
|
存储 缓存 算法
CPU优化
【10月更文挑战第7天】
406 1
|
小程序 JavaScript Java
【Java】服务CPU占用率100%,教你用jstack排查定位
本文详细讲解如何使用jstack排查定位CPU高占用问题。首先介绍jstack的基本概念:它是诊断Java应用程序线程问题的工具,能生成线程堆栈快照,帮助找出程序中的瓶颈。接着,文章通过具体步骤演示如何使用`top`命令找到高CPU占用的Java进程及线程,再结合`jstack`命令获取堆栈信息并进行分析,最终定位问题代码。
1390 2
【Java】服务CPU占用率100%,教你用jstack排查定位
|
监控 Java Linux
CPU被打满/CPU 100%:高效诊断与优化策略
【8月更文挑战第28天】在日常的工作与学习中,遇到CPU使用率飙升至100%的情况时,往往意味着系统性能受到严重影响,甚至可能导致程序响应缓慢或系统崩溃。本文将围绕这一主题,分享一系列高效诊断与优化CPU使用的技术干货,帮助大家快速定位问题并恢复系统性能。
707 1

热门文章

最新文章