码迷,mamicode.com
首页 > 系统相关 > 详细

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

时间:2020-12-25 11:57:27      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:脚本   cpu高   nts   http   https   str   有关   toc   cto   

前言

本文记录下生产一次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:\\data\\allocate\\test-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使用率低的分析

标签:脚本   cpu高   nts   http   https   str   有关   toc   cto   

原文地址:https://www.cnblogs.com/zhangyjblogs/p/14163576.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!