CPU性能分析实战

CPU性能分析实战

一、CPU性能分析实战准备

1、使用监控平台:grafana + prometheus + node_exporter

  1.1 被监控的机器上放置node_exporter,输入命令 nohup ./node_exporter & 端口 9100启动

  1.2 监控平台机器上,启动grafana和 prometheus ,输入命令systemctl restart grafana-server,配置 prometheus.yml,输入命令nohup ./prometheus & 启动

  1.3 界面配置,参考:https://www.cnblogs.com/hailangboke/p/16219734.html 界面配置

 

二、CPU专项测试

1、CPU负载:CPU的上下文切换会导致CPU性能较差,导致系统负载高有三种情况: 进程上下文切换、线程上下 文切换、中断上下文切换

 

2、进程上下文切换:有至少2个进程,进程之间有关联关系,从一个进程,切换到另外一个进程。

  2.1 进程A,切换到进程B

    2.1.1 先保存A的资源,然后打开进程B的资源,最后打开进程B中的线程来执行任务。这样一个过程,会导致部分时间浪费在资源保存与切换之间,会出现卡顿。如果切换的进程多,总的卡顿时间就会变多。

    2.1.2 实操:

      2.1.2.1 安装 stress-ng 工具:这个工具是一个 性能模拟工具,模拟用户端并发请求导致CPU、磁盘等的性能问题,在服务器上的效果。

      2.1.2.2 安装命令:yum install -y epel-release.noarch && yum -y update   ;yum install -y stress-ng

    2.1.3 模拟真实场景命令:(( proc_cnt = `nproc`*10 )); stress-ng --cpu $proc_cnt -- pthread 1 --timeout 150  启动N*10个进程,在只有N个核的系统上,会产生大量的进程切换,模拟进程间竞争CPU的场景

      2.1.3.1 --cpu N start N workers spinning on sqrt(rand())启动n个CPU,来执行 sqrt(随机数)

      2.1.3.2 --pthread N start N workers that create multiple threads 每个CPU,启动多少个线程

      2.1.3.3 --timeout N 执行多长时间停止

    2.1.4 分析命令

      2.1.4.1 top命令:load average数值变化非常大,说明系统有非常大的压力;us + sy ≈100 us高,sy低, 表示新增了多个进程

      2.1.4.2 vmstat 1 命令:procs 中 r队列,有非常大的数据变化,b列基本没有变化;memory数据 变化不明显,swap、io数据都不明显;system中in和cs都有明显数据; cpu中 us+ sy ≈100

      2.1.4.3 pidstat -w 1:cswch/s 有一个进程的数据非常大,总体来说,自愿上下文切换的总数大于非自愿上下文切换

    2.1.5 总结:进程上下文切换高,vmstat命令、pidstat命令,我们看到有非常大的上下 文切换数据;在监控平台中,我们也看到有非常大的上下文数据变化,说 明,我们现在主要为问题是上下下文切换;vmstat命令看到 r队列很大,说明我们有大量的进程在抢占我们的CPU资 源,CPU不够用;pidstat命令,看到是自愿上下文切换要高。

    2.1.6 现象对应企业:在一台服务器上,部署了很多服务,这些同时使用的进程数量,超过我们CPU的数量,导致进程之间互相争抢CPU,从而出现,自愿上下文切换比较高,vmstat中r数据偏大的现象。常见于php项目

    2.1.7 解决方法:把服务器中的一些进程迁移出去,部署到另外的机器上;增加服务器的CPU数量

 

3、线程上下文切换

  3.1 进程A的线程,与进程B的线程切换

    3.1.1 首先要保存进程A的资源,然后,打开进程B的资源,然后,在使用B的线 程来执行任务

    3.1.2 同一个进程中,线程之间的切换:一个进程,启动了多个线程,这些线程都是使用同一个进程资源,保 存线程私有资源,然后打开另外线程的资源。

  3.2 实操:stress-ng --cpu `nproc` --pthread 10 --timeout 150 同时开启10个线程

    3.2.1 分析命令

      3.2.1.1 top命令:load1值变得非常大 ,us+sy ≈100,sy的数据要明显大一些。 进程列表中,也有10个线程。

      3.2.1.2 vmstat命令:procs的r队列也有明显的数据,而且非常大,b没有数据变化;memory中, free有变小;swap、io没有变化;system中in有明显数据,cs有非常大的数据变化。上下文切换要比进程上下文切换要大很多。

      3.2.1.3 pidstat -w 1 命令, 几乎每个线程都有 自愿上下文切换,总的数量,远大于非自愿上下文数量:进程上下文切换时候,自愿上下文切换,有一个非常大,其他的都是 非自愿上下文切换,非自愿上下文切换的数据还是比较大的;线程上下文切换, 自愿上下文切换非常大,每个线程都有大量的自愿上下文切换,非自愿上下文切换,几乎没有

  3.3 现象对应企业场景:在企业服务器中,一个进程,启动了非常多线程,来处理事物。这种,常 见于,java项目。

  3.4 解决方法:增加CPU;适当的减少线程数量;适当增加内存;最好的办法,定位到问题根源,解决掉代码问题

 

4、io密集型:中断上下文切换

  4.1 当我们对io使用率很高,会导致我们系统很卡,系统压力很大,一个明显的现象就是你用终端连接时候,命令无响应。因为,我们系统负载有cpu负载 + io负载

  4.2 实操:stress-ng -i 10 --hdd 1 --timeout 150

    4.2.2 分析命令

      4.2.2.1 top命令:开始时,sy数据很大,然后,wa数据很大,sy+wa ≈100。

      4.2.2.2 vmstat中 free变化比较明显,buff清空了,cahce也有变化,io中bo有明显的数据,说明有磁盘写操作

      4.2.2.3 mpstat 中iowait有明显的数据,说明有等待

      4.2.2.4 iostat命令中 await有明显的数据, w_await有明显数据,说明是有大量的磁盘写操作。

      4.2.2.5 大量的写磁盘操作,导致了系统负载升高。

  4.3 对应场景:有大量的磁盘写操作,导致系统负载升高的情况。

 

5、CPU实战-us态高分析(直接定位到代码)

  5.1 项目部署:jvmpertest + tomcat +jdk1.8

    5.1.1 解压 tomcat的包

    5.1.2 把jvmpertest.war包 放tomcat的webapps

    5.1.3 启动tomcat, 默认端口 8080

    5.1.4 http://serverip:8080/JvmPertest/pertest1 测试接口  

    5.1.5 http://serverip:8080/JvmPertest/PerThreadTest 创建线程

    5.1.6 jmeter编写脚本

  5.2 分析

    5.2.1 使用top命令或者jps命令可以或得进程ID 4757

    5.2.2 但是进程只是资源的拥有者,并不是执行者,所以需要找到进程中的线程,使用命令:top -Hp +进程id可以得到进程中的所有线程

    5.2.3  调用接口:http://serverip:8080/JvmPertest/PerThreadTest,发现,我们top命令中 us态,已经达到100%,执行 top -Hp 进程id 找到了新增的线程的id 4854

    5.2.4 要把线程id 转换为16进制, 因为,我们要定位线程的问题,这些线程的栈 信息是在内存中,是16进制显示在内存中,命令:printf"%x\n" +线程id

    5.2.5 输入命令jstack 进程id | grep 线程id的16进制  -A20 从前往后显示20行(jstack 4757 |grep 12f6 -A20 ) 源代码显示现在,我们这个项目导致CPU的us态很高的原因,是 ThreadPerTest.java文件中的第 15行代码

"Thread-9" #26 daemon prio=5 os_prio=0 tid=0x00007fc7d01f4800 nid=0x12f6 waiting on condition [0x00007fc7b7efd000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.pertest.ThreadPerTest$LazyTask.run(ThreadPerTest.java:15)
	at java.lang.Thread.run(Thread.java:750)

"Thread-8" #25 daemon prio=5 os_prio=0 tid=0x00007fc7d0706000 nid=0x12f5 runnable [0x00007fc7b7ffe000]
   java.lang.Thread.State: RUNNABLE
	at com.pertest.ThreadPerTest$ThreadPerTask.run(ThreadPerTest.java:8)
	at java.lang.Thread.run(Thread.java:750)

"http-nio-8080-exec-2" #24 daemon prio=5 os_prio=0 tid=0x00007fc7d4072800 nid=0x12f4 waiting on condition [0x00007fc7c4114000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000edf4f140> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1112)

  

 

 

 

 

  

posted @ 2022-06-20 21:39  无名。。。  阅读(380)  评论(8编辑  收藏  举报