问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

CPU 100% 优化排查实战

创作时间:
作者:
@小白创作中心

CPU 100% 优化排查实战

引用
CSDN
1.
https://blog.csdn.net/gaosw0521/article/details/144935416

本文通过一个实际案例,详细介绍了在处理服务器CPU使用率异常高的问题时,如何通过一系列排查步骤定位到Disruptor队列的等待策略问题,并最终通过调整等待策略和应用拆分等优化方案解决了问题。文章内容详实,包含了具体的排查步骤、代码示例和优化方案,具有较高的技术参考价值。

1 问题背景

收到运维同学的报警,某些服务器负载非常高。经过初步排查,发现服务器上运行的只有我们的 Java 应用程序。于是,我们开始了一系列的排查和优化工作。

2 排查步骤

2.1 获取进程信息

首先,使用
ps
命令获取应用的 PID:

  
ps -ef | grep java
  

2.2 查看线程 CPU 使用情况

使用
top -Hp pid
命令查看该进程的线程信息,并按 CPU 使用率排序(输入大写
P
):

  
top -Hp <pid>
  

发现某些线程的 CPU 使用率高达 99.9%。

2.3 导出线程栈信息

为了进一步分析,使用
jstack
命令将线程栈信息导出到日志文件中:

  
jstack <pid> > pid.log
  

2.4 分析线程栈

在 99.9% CPU 使用率的线程中,随机选择一个线程(例如
pid=194283
),将其转换为 16 进制(
2f6eb
),并在线程快照中查找对应的线程信息。

发现这些线程都与 Disruptor 队列相关,且都在执行
java.lang.Thread.yield
方法。

2.5 使用分析工具

为了更直观地查看线程状态,将线程快照信息上传到fastthread.io进行分析。分析结果显示,几乎所有消耗 CPU 的线程都与 Disruptor 队列相关,且都在执行
yield
方法。

2.6 初步判断

初步判断,大量线程执行
yield
方法后,互相竞争导致 CPU 使用率增高。通过对堆栈的分析,发现确实与 Disruptor 有关。

3 Disruptor 使用方式

3.1 引入依赖


pom.xml
文件中引入 Disruptor 的依赖:

  
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.4.2</version>
</dependency>
  

3.2 定义事件

定义事件
LongEvent

  
public static class LongEvent {
    private long value;
    public void set(long value) {
        this.value = value;
    }
    @Override
    public String toString() {
        return "LongEvent{value=" + value + '}';
    }
}
  

3.3 定义事件工厂

定义事件工厂
LongEventFactory

  
public static class LongEventFactory implements EventFactory<LongEvent> {
    @Override
    public LongEvent newInstance() {
        return new LongEvent();
    }
}
  

3.4 定义事件处理器

定义事件处理器
LongEventHandler

  
public static class LongEventHandler implements EventHandler<LongEvent> {
    @Override
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch) {
        System.out.println("Event: " + event);
    }
}
  

3.5 定义事件发布者

定义事件发布者:

  
public static void main(String[] args) throws InterruptedException {
    // 指定 Ring Buffer 的大小
    int bufferSize = 1024;
    // 构建 Disruptor
    Disruptor<LongEvent> disruptor = new Disruptor<>(
            new LongEventFactory(),
            bufferSize,
            Executors.defaultThreadFactory());
    // 连接事件处理器
    disruptor.handleEventsWith(new LongEventHandler());
    // 启动 Disruptor
    disruptor.start();
    // 获取 Ring Buffer
    RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
    // 生产事件
    ByteBuffer bb = ByteBuffer.allocate(8);
    for (long l = 0; l < 100; l++) {
        bb.putLong(0, l);
        ringBuffer.publishEvent((event, sequence, buffer) -> event.set(buffer.getLong(0)), bb);
        Thread.sleep(1000);
    }
    // 关闭 Disruptor
    disruptor.shutdown();
}
  

简单解释下:
LongEvent
:这是要通过 Disruptor 传递的数据或事件。
LongEventFactory
:用于创建事件对象的工厂类。
LongEventHandler
:事件处理器,定义了如何处理事件。
Disruptor
构建:创建了一个 Disruptor 实例,指定了事件工厂、缓冲区大小和线程工厂。
事件发布:示例中演示了如何发布事件到 Ring Buffer。

4 问题定位与优化

4.1 问题定位

通过代码审查,发现每个业务场景内部都使用了 2 个 Disruptor 队列来解耦。假设有 7 个业务,则创建了 14 个 Disruptor 队列,每个队列有一个消费者,总共 14 个消费者。配置的消费等待策略为
YieldingWaitStrategy
,这种策略会执行
yield
来让出 CPU。

4.2 本地模拟

为了验证问题,在本地创建了 15 个 Disruptor 队列,并结合监控观察 CPU 使用情况。发现 CPU 使用率确实很高,且线程状态与生产环境一致。

注意看代码 YieldingWaitStrategy:
以及事件处理器:
创建了 15 个 Disruptor 队列,同时每个队列都用线程池来往 Disruptor队列 里面发送 100W 条数据。消费程序仅仅只是打印一下。

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

同时 dump 线程发现和生产环境中的现象也是一致的:消费线程都处于 RUNNABLE 状态,同时都在执行 yield。

4.3 调整等待策略


通过查询 Disruptor 官方文档,发现
YieldingWaitStrategy
适用于消费线程数量小于 CPU 核心数的情况。而当前场景中,消费线程数远超过 CPU 核心数。因此,将等待策略调整为
BlockingWaitStrategy

,发现 CPU 使用率明显降低。

运行后的结果如下:
dump 线程后,发现大部分线程都处于 waiting 状态。

4.4 进一步优化

将 Disruptor 队列调整为 1 个,并保持
YieldingWaitStrategy
策略,发现 CPU 使用率保持平稳。

5 优化方案

  1. 调整等待策略:将等待策略从
    YieldingWaitStrategy
    调整为
    BlockingWaitStrategy
    ,以降低 CPU 使用率。
  2. 应用拆分:将现有业务拆分为多个应用,每个应用处理一种业务类型,分别部署,以实现隔离。
  3. 线程池优化:调整线程池配置,减少线程数量,避免空闲线程占用资源。

6 总结

通过本次排查,我们发现 CPU 使用率过高的问题与 Disruptor 的等待策略和线程数量有关。通过调整等待策略和应用拆分,可以有效降低 CPU 使用率。希望本次排查思路能为大家提供一些启发。

7 思维导图

8 参考链接

一次生产环境中 CPU 占用 100% 排查优化实践

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号