一次生产 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…


相关文章
|
1天前
|
缓存 弹性计算 监控
云服务器 CPU 使用率高的问题排查与优化
云服务器 CPU 使用率高的问题排查与优化
7 0
|
4天前
|
Arthas Java 数据库连接
「性能指标」CPU飙高排查实战
在新应用接入高流量业务后,CPU利用率在压力测试中飙升。通过`top`命令发现Java进程占用CPU高。使用Arthas工具定位到JDBC的TCP套接字读取导致阻塞,问题源于频繁的数据库交互。代码优化后,初始化Sequence实例减少数据库交互,CPU使用下降。后续发现预发布环境的日志采集工具开启导致额外CPU消耗,关闭该功能后问题完全解决。排查CPU问题需耐心和系统性分析。
「性能指标」CPU飙高排查实战
|
17天前
|
SQL 数据处理 API
实时计算 Flink版产品使用问题之holo的io以及cpu使用较为稳定,sink端busy一直在20%左右,有时候50%,该如何优化
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
28天前
|
SQL Java Linux
Linux系统cpu飙升到100%排查方案
Linux系统cpu飙升到100%排查方案
20 0
|
2月前
|
SQL 运维 NoSQL
【Redis 故障排查】「连接失败问题排查和解决」带你总体分析CPU及内存的使用率高问题排查指南及方案
【Redis 故障排查】「连接失败问题排查和解决」带你总体分析CPU及内存的使用率高问题排查指南及方案
80 0
|
6天前
|
SQL 关系型数据库 分布式数据库
PolarDB产品使用问题之在一个集群上创建多个数据库实例,是否可以做cpu和内存的配额指定
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
13天前
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
汇编语言(第四版) 实验一 查看CPU和内存,用机器指令和汇编指令编程
|
19天前
|
Python
python3获取内存和cpu利用率记录日志文件psutil
python3获取内存和cpu利用率记录日志文件psutil
17 1
|
21天前
|
运维 Serverless Nacos
Serverless 应用引擎产品使用合集之在访问量过大的情况下,函数配置的cpu和内存会自动扩容吗
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
2月前
|
关系型数据库 MySQL Java
实时计算 Flink版操作报错之整内存和cpu分配之后启动报错如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。