记录生产一次linux负载高cpu使用率低的分析 [操作系统入门]

编程

目录

  • 前言
  • load average定义
  • 平均负载与CPU使用率关系
    • case1:CPU密集型java应用
    • case2:IO密集型java应用
      • case2.1.磁盘IO密集型应用
      • case2.2.网络IO密集型应用
    • case3:线程上下文大量切换也会导致cpu使用率增高,平均负载也变高
  • 总结

前言

本文记录下生产一次cpu使用率低,但是load average高的情况,如下两图,load average很高,但是cpu使用率很低

先说下这个机器配置:16C16G,用于jenkins构建服务器

通常指的load average是和cpu有关,cpu越高,load average越高,但是本次问题情况正好相反,问题出现在哪里? 是不是对平均负载理解的不正确呢?

load average定义

平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和CPU使用率并没有直接关系。

可运行状态的进程,是指正在使用CPU或者正在等待CPU的进程,也就是我们常用ps命令看到的,处于R状态(Running 或 Runnable)的进程。

不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的I/O响应。

比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题。

因此前面自己的理解是错误的,比如1min内的平均负载load average指的是1min内正在使用CPU进程+正在等待CPU的进程+等待IO的进程,同理5min、15min也是如此计算。

但是,我们平时是java应用,一个应用开启多个线程,这个就涉及到线程和进程的关系了。

linux内进程就是我们ps -fe 看到的,线程是属于进程的,一个进程至少有一个线程,对于我们java应用来说,一个java进程通常有多个线程。线程又称为Light—Weight Process,cpu处理线程是使用分片法,线程也涉及到使用cpu资源和IO(线程向磁盘读写数据),因此平均负载也简单定义为1min内正在使用CPU线程+正在等待CPU的线程+等待IO的线程。

平均负载与CPU使用率关系

case1:对于CPU密集型java应用,并发高的情况下,cpu使用率飚高,此时load average也高,此时平均负载与CPU使用率是一致的。

case2:对于IO密集型java应用,由于等待IO响应的线程数增加,导致平均负载高,但是CPU使用率不一定高。

case3:线程上下文大量切换也会导致平均负载升高,此时的CPU使用率也会比较高。

下面对这三种情况以实际例子说明:

case1:CPU密集型java应用

对于case1,比如写个使用fastjson循环解析json字符串的应用,使用jmeter压测,cpu很容易飚高,查看到load average也变高。

比如下面这个代码,直接就把cpu打满了,在2C2G服务器上使用jmeter并发线程2,测试,load average在1,5min内是4.15, 2.29。

/**

* 使用正则表达式让cpu达到100%,这个结果计算要15s

*/

@Override

public void cpuRegex() {

String regex = "(w+,?)+";

String val = "abcdefghijklmno,abcdefghijklmno+";

log.info("正则结果->{}", val.matches(regex));//val.matches耗费cpu

}

case2:IO密集型java应用

case2.1.磁盘IO密集型应用

对于case2,写个频繁写入和读取磁盘数据的应用,使用jmeter压测,使用iostat检测磁盘,查看到load average也变高。

测试代码如下

@Override

public void io() {

try {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

URL url = (classLoader != null ?

classLoader.getResource("test-service.2020-01-09.log") :

ClassLoader.getSystemResource("test-service.2020-01-09.log"));

String logText = IOUtils.toString(url, Charset.forName("utf8"));//读取resources下的test-service.2020-01-09.log文件内

File targetFile = null;

String os = System.getProperty("os.name");

if (StrUtil.containsIgnoreCase(os, "linux")) {

targetFile = new File("/data/log/allocate/test-service-"+number.getAndIncrement()+".log");

} else {

targetFile = new File("D:dataallocate est-service-"+number.getAndIncrement()+".log");

}

boolean exists = targetFile.exists();

if (!exists) {

targetFile.createNewFile();

}

FileOutputStream out = new FileOutputStream(targetFile);

out.write(logText.getBytes("utf8"));//阻塞io操作,每次请求都写文件,而且是同步写的方式,这样磁盘IO就很大了

out.close();

}

catch (IOException ex) {

throw new IllegalArgumentException("Unable to load factories from location [" +

"test-service.2020-01-09.log" + "]", ex);

}

}

case2.2.网络IO密集型应用

上面是磁盘IO阻塞,那么如果是网络IO阻塞,是否会对平均负载造成影响呢?测试如下

测试代码

@Override

public void socketIO() {

Socket socket = new Socket();

try {

socket.setSoTimeout(60000);//设置读取超时时间为60s

socket.setTcpNoDelay(true);

socket.connect(new InetSocketAddress("xxx.xxx.xxx.xxx", 80));//连接另外一个nginx服务器的80端口

InputStream in = socket.getInputStream();

byte[] b = new byte[1024];

int read = in.read(b);//阻塞在读上

} catch (IOException e) {

log.error("读取超时,异常{}", e.getMessage());

} finally {

try {

socket.close();

} catch (IOException e) {

log.error("socket关闭异常", e.getMessage());

}

}

}

在使用jmeter压测,发现此时平均负载很低,cpu也很低,说明网络阻塞并不会影响平均负载。

case3:线程上下文大量切换也会导致cpu使用率增高,平均负载也变高

比如下面这样代码,一次请求内线程阻塞了2次,释放了2次cpu资源使用,这样在并发下就会导致上下文切换频繁,线程上下文频繁切换可通过vmstat查看cs指标,这个在我并发200的测试条件下,线程上下文切换为3w多。

@Override

public void cpuSwitch() {

try {

//业务代码

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

int i = 1+1;

//业务代码

Thread.yield();

int j = 1+1;

}

总结

平均负载可以基本认为=正在使用CPU+者正在等待CPU的进程+磁盘IO,对于一个4C8G的服务器,如果负载低于2.8,说明正常,超过的不多,先看cpu,再看io,通常cpu100%虽然平均负载高,但是不会高的很离谱,高的离谱了,比如自己测试的磁盘IO例子(同步向文件写数据),那么基本说明是磁盘IO问题。cpu高,可以通过jstack命令或show-busy-java-threads脚本来定位,io高只能通过iostat、iotop来定位。

总结下磁盘IO高的几个原因:

1.频繁的向磁盘写入或读取大量数据

2.磁盘坏了,磁盘性能不好(碎片太多,nfs磁盘)

对于我们生产这次平均负载增高,原因是虚机所在的母机上的一块磁盘坏了导致平均负载增高。

具体的cpu和io实战分析,请看另外的笔记 CPU和磁盘IO实战笔记总结

记录生产一次linux负载高cpu使用率低的分析

以上是 记录生产一次linux负载高cpu使用率低的分析 [操作系统入门] 的全部内容, 来源链接: utcz.com/z/519442.html

回到顶部