做为后端开发工程师,当收到线上服务器CPU负载太高告警时,你会这么作?重启服务,忽略告警?不过在我看来一个合格的工程师是必定要定位到具体问题所在的,从而 fix 它。下面记录一下线上服务器 CPU 负载太高排查过程,把排查流程理清楚,之后遇到问题将会迅速定位到问题所在,快速解决。java
代码层面常见的场景有:linux
这里使用 JAVA 简单模拟程序死循环带来的系统高负载状况,代码以下:后端
/** * @program: easywits * @description: 并发下的 HashMap 测试.... * @author: zhangshaolin * @create: 2018-12-19 15:27 **/ public class HashMapMultiThread { static Map<String, String> map = new HashMap<>(); public static class AddThread implements Runnable { int start = 0; public AddThread(int start) { this.start = start; } @Override public void run() { //死循环,模拟CPU占用太高场景 while (true) { for (int i = start; i < 100000; i += 4) { map.put(Integer.toString(i), Integer.toBinaryString(i)); } } } public static void main(String[] args) throws InterruptedException { //线程并发对 HashMap 进行 put 操做 若是一切正常,则获得 map.size() 为100000 //可能的结果: //1. 程序正常,结果为100000 //2. 程序正常,结果小于100000 Thread thread1 = new Thread(new AddThread(0), "myTask-1"); Thread thread2 = new Thread(new AddThread(1), "myTask-2"); Thread thread3 = new Thread(new AddThread(2), "myTask-3"); Thread thread4 = new Thread(new AddThread(3), "myTask-4"); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread1.join(); thread2.join(); thread3.join(); thread4.join(); System.out.println(map.size()); } } }
一样使用 JAVA 程序简单模拟线程死锁场景,代码以下:服务器
/** * @program: easywits * @description: 死锁 demo .... * 1.两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock做为同步代码块的锁; * 2.线程1的run()方法中同步代码块先获取lock1的对象锁,Thread.sleep(xxx),时间不须要太多,50毫秒差很少了,而后接着获取lock2的对象锁。 * 这么作主要是为了防止线程1启动一会儿就连续得到了lock1和lock2两个对象的对象锁 * 3.线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,固然这时lock1的对象锁已经被线程1锁持有,线程2确定是要等待线程1释放lock1的对象锁的 * <p> * 线程1″睡觉”睡完,线程2已经获取了lock2的对象锁了,线程1此时尝试获取lock2的对象锁,便被阻塞,此时一个死锁就造成了。 * @author: zhangshaolin * @create: 2018-12-20 11:33 **/ public class DeadLock { static Object lock1 = new Object(); static Object lock2 = new Object(); public static class Task1 implements Runnable { @Override public void run() { synchronized (lock1) { System.out.println(Thread.currentThread().getName() + " 得到了第一把锁!!"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println(Thread.currentThread().getName() + " 得到了第二把锁!!"); } } } } public static class Task2 implements Runnable { @Override public void run() { synchronized (lock2) { System.out.println(Thread.currentThread().getName() + " 得到了第二把锁!!"); synchronized (lock1) { System.out.println(Thread.currentThread().getName() + " 得到了第一把锁!!"); } } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Task1(), "task-1"); Thread thread2 = new Thread(new Task2(), "task-2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(Thread.currentThread().getName() + " 执行结束!"); } }
以上两种场景代码执行后,不出意外,系统CPU负载将会飙升,个人机器,4核CPU已经明显感受到卡顿了,因此线上应该杜绝出现死循环代码。。并发
top
命令监控当前系统负载状况执行第一种场景测试代码。ide
在 linux
命令行键入 top
指令后,就开始实时监控当前系统的负载信息,监控到的负载信息以下图所示:测试
从图中的监控信息能够快速大体的了解到,PID
为17499
的进程CPU负载高达328+%
,是一个 JAVA
程序。简单介绍下监控信息以下:this
在监控页面下 按键盘数字 1
能够看到每一个CPU的负载状况,以下图:spa
能够看到开了四个线程,无限循环以后,个人机器中四个核心CPU,每颗负载接近百分百。命令行
top
命令监控进程中负载太高的线程top -H -p pid
: 查看指定进程中每一个线程的资源占用状况(每条线程占用CPU时间的百分比),监控结果以下图:
以上监控指令输出的指标针对的是某个进程中的线程,从图中看能够快速得出结论:四个 JAVA
线程CPU负载极高,线程ID分别为:17532
,17535
,17533
,17534
,注意这里打印出来的线程ID为十进制的哦!
进程pid
&&线程id
查看线程堆栈信息jstack pid
:查看指定进程中线程的堆栈信息,这个命令最终会打印出指定进程的线程堆栈信息,而实际线上状况发生时,咱们应当把快速把堆栈信息输出到日志文本中,保留日志信息,而后迅速先重启服务,达到临时缓解服务器压力的目的。jstack 17499 > ./threadDump.log
:将线程堆栈信息输出到当前目录下的 threadDump.log 文件。注意:jstack 打印出的线程id号为十六进制,而 top 命令中打印出来的线程号为十进制,须要进行转换后,定位指定线程的堆栈信息
这里分析日志文件后,过滤出四个线程堆栈信息以下图:
从这四个线程执行的堆栈信息,很明显的看出:致使CPU飙升的程序正在执行 HashMap 的 put 操做。
友情提示:测试代码最好不要在公司的线上环境作测试哦!
更多原创文章会在公众号第一时间推送,欢迎扫码关注 张少林同窗