前言大家都知道大促期间服务器的cpu和load会因为流量的增加而增加。本文将与您一起整合CPU和负载。负荷的概念是为今年的各种大促销做准备,同时增加自己的技能储备。不过cpu和load确实是需要积累的。笔者的经验还是很少,感觉还是有很多地方写得不好或者不合适。如有错误,希望大家帮助指正。1、top命令既然提到了cpu和load,那肯定是要监控的。没有监控,你就不知道cpu和load,后面的一切都无从谈起。top命令是最常用的查看cpu和负载的命令。拿我自己虚拟机安装的ubuntu系统,执行top命令(默认3秒刷新一次,-d可以指定刷新时间):我做了一个表格,比较详细的解释了各个部分的意思,以及重要属性用红色粗体标出:内存和SWAP输出格式相同,所以写在一起。2、如何计算cpu当我们执行top命令的时候,可以看到里面的值(主要是cpu和load)是在不断变化的,所以有必要简单了解下linux系统中cpu的计算方法。cpu分为系统cpu和进程与线程cpu。系统cpu的统计值位于/proc/stat下(以下截图不完整):cpu和cpu0后面的数字分别对应前面的us、sy和ni。哪个值对应哪个值并不重要。有兴趣的可以上网查文档。进程cpu统计值位于/proc/{pid}/stat下:线程cpu统计值位于/proc/{pid}/task/{threadId}/stat下:所有值都在这里是从系统启动时间到当前时间的一个值。所以cpu的计算方法是采样两个足够短的时间t1和t2:将t1的所有cpu使用率相加得到s1;将t2的所有cpu使用率相加得到s2;s2-s1获取该时间区间内所有时间的totalCpuTime;第一空闲idle1-第二空闲idle2,获取采样时间内的空闲时间;cpu使用率=100*(totalCpuTime-idle)/totalCpuTime。us、sy、ni等其他时间的计算方法类似。综上所述,cpu的值反映了一定采样时间内cpu的使用情况。所以,有时候cpu很高,但是打印线程栈的时候,发现是cpu高的线程在等待查询数据库。不要惊讶,因为cpu统计的是采样时间内的数据。假设top观察到用户空间cpu有一段时间一直很高,说明用户程序在这段时间一直在占用cpu做事情。3、负载的理解关于负载的含义,其实有些文章将其与过桥联系起来,更贴切也更容易理解:单核处理器可以比作单车道,车辆行驶反过来。在这条单车道上,后车只能在前车通过后才能行驶。如果前面没有车辆,那么你就顺利通过;如果车辆很多,那么你需要等前面的车辆通过后才能超车。因此,需要一些特定的代码来表示当前的车流量,例如:等于0.00,表示目前桥上没有车流量。其实这种情况在0.00和1.00之间是一样的。总而言之,很顺利,过往车辆完全不用等待就可以通过;等于1.00,表示刚好在桥梁的承载范围内。这种情况还不错,只是车流会有些拥堵,但这种情况可能会导致车流越来越慢;如果大于1.00,则表示桥梁超载,交通严重拥堵。那么情况有多糟糕呢?比如2.00的情况,说明车流量已经超过了大桥通行能力的两倍,所以过桥的车辆会多出一倍以上焦急等待。但比喻毕竟是比喻。从这个比喻中,我们了解到负载代表了系统的一种能力,但是我们并不知道负载的计算中会包含什么样的任务。至于具体的任务会如何计入load的计算,可以用manuptime命令看看linux对load的解释:大体意思是系统load是平均正在运行或不可中断的进程数(标有红色部分表示负载中包含的内容)。处于running状态的进程意味着它正在使用cpu或者等待使用cpu,而处于uninterruptible状态的进程意味着它正在等待IO,比如磁盘IO。负载的平均值分三个时间段显示,即我们看到的1分钟、5分钟、15分钟。负载值与CPU核心数有关。单核cpu的load=1表示系统一直处于负载状态,而4核cpu的load=1表示系统有75%空闲。特别说明一下,load指的是所有core的平均值,和cpu的值是不一样的。还有一点很重要,查了资料发现虽然上面一直强调“进程”,但是进程中的线程数也会作为不同的进程来计算。如果一个进程同时产生1000个线程运行,那么运行队列长度为1000,平均负载为1000。4.请求数与负载的关系我之前一直有个误区:当千ofrequestscomeandareinthequeue,后面的请求无法处理,负载值必然增加。深思熟虑后,这个观点确实是错误的,所以我就把它写成一段话分享给大家。以Redis为例,我们都知道Redis是单线程模型,也就是说可以同时有无数个请求,但同时只会处理一条命令。图片来源:https://www.processon.com/view/5c2ddab0e4b0fa03ce89d14f单线程收到就绪命令后,会将命令传递给事件派发器,事件派发器根据类型执行相应的命令处理逻辑命令的。由于只有一个线程,只要有足够多的命令排队让这个线程一个接一个地处理命令,那么loadperformance就等于1。整个过程中,回头看load的值,它与请求的数量无关。真正与负载相关的是工作线程的数量。主线程是工作线程,定时器是工作线程,GC线程也是工作线程。负载以线程/进程为统计指标,无论请求多少,最终都需要线程来处理,工作线程的处理性能直接决定了最终的负载值。例如,假设一个服务中有一个线程池,线程池中的线程数固定为64:正常情况下,一个任务的执行时间为10ms,线程在10ms内获取到任务并完成处理,而很快就回到线程池等待下一个任务的到来,自然是很少有正在运行或者等待IO的线程了。从一个统计周期来看,负载性能很低;由于系统在某段时间内出现问题,10秒内无法处理一个任务,即线程一直在处理任务。负载统计周期反映的值=64(64线程以外的场景不考虑)。因此,总而言之,理解负载值、请求数和线程数之间的关系是非常重要的。只有把这些想清楚了,才能正确的进行下一步。5、高负载高cpu问题排查首先提出一个观点:高cpu不是问题,高cpu导致的高负载才是问题所在,负载是判断系统能力指标的依据。为什么这么说呢,以单核cpu为例,当我们每天的cpu在20%或者30%的时候,其实就是cpu资源的浪费,也就是说大部分时间cpu是没有在做事情的。理论上,一个系统最终的cpu利用率可以达到100%,这意味着cpu被充分利用来处理计算密集型任务,比如f??or循环、md5加密、新建对象等等。但是这种情况其实是不可能发生的,因为不消耗CPU的IO几乎不可能存在于应用程序中,比如读数据库或者读文件,所以CPU不是越高越好,通常75%是需要引起警戒的经验值。注意,上面的“warning”是说CPU高不一定是问题,但是需要检查一下,尤其是在日常生活中,因为通常每天的流量都不大,CPU不可能达到这样的一个高水平。如果只是普通的代码,确实在处理正常的业务,那是没有问题的。如果代码中存在死循环(比如JDK1.7中经典的HashMap扩容导致的死循环问题),那么一直有几个线程在占用CPU,最终会造成负载的增加。在Java应用中,排查高CPU的思路通常比较简单,有比较固定的方法:ps-ef|grepjava,查询Java应用的进程pid;top-H-ppid,查询CPU使用率最高的线程pid;将十进制的线程pid转换为十六进制的线程pid,例如2000=0x7d0;jstack进程pid|grep-A20'0x7d0',找到与nid匹配的线程,查看栈,定位cpu高的原因。网上有很多文章说到这里就打住,但实际上并不是这样的。因为cpu是一个时间段内的统计值,而jstack是一个瞬时栈,只记录瞬时状态,两者根本不是一维的事情,所以完全可以看出代码停留在下面从打印的堆栈行号中放置:没有消耗cpu的网络IO;对于(inti=0,size=list.size();i
