编辑
2023-04-20
运维
00
请注意,本文编写于 518 天前,最后修改于 518 天前,其中某些信息可能已经过时。

目录

火焰图
6.1 说明
6.2 安装依赖库
6.3 安装
6.4 CPU 级别火焰图
6.4.1 on-CPU
6.4.2 off-CPU
6.5 内存级别火焰图
6.6 性能回退 - 红蓝差分火焰图
案例分析
7.1 接入层 nginx 集群异常现象
7.2 分析 nginx 相关指标
7.3 分析系统 cpu 情况
7.4 火焰图分析 cpu
7.5 案例总结
7.5.1 深入分析
7.5.2 解决方式

火焰图

6.1 说明

火焰图(Flame Graph 是 Bredan Gregg 创建的一种性能分析图表,因为它的样子近似 ? 而得名。

火焰图主要是用来展示 CPU 的调用栈。

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。

x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有” 平顶”(plateaus),就表示该函数可能存在性能问题。颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

常见的火焰图类型有 On-CPU、Off-CPU、Memory、Hot/Cold、Differential 等等。

6.2 安装依赖库

//安装systemtap,默认系统已安装yum install systemtap systemtap-runtime //内核调试库必须跟内核版本对应,例如:uname -r 2.6.18-308.el5kernel-debuginfo-2.6.18-308.el5.x86_64.rpmkernel-devel-2.6.18-308.el5.x86_64.rpmkernel-debuginfo-common-2.6.18-308.el5.x86_64.rpm //安装内核调试库debuginfo-install --enablerepo=debuginfo search kerneldebuginfo-install --enablerepo=debuginfo search glibc

6.3 安装

git clone https://github.com/lidaohang/quick_location.gitcd quick_location

6.4 CPU 级别火焰图

cpu 占用过高,或者使用率提不上来,你能快速定位到代码的哪块有问题吗?

一般的做法可能就是通过日志等方式去确定问题。现在我们有了火焰图,能够非常清晰的发现哪个函数占用 cpu 过高,或者过低导致的问题。

6.4.1 on-CPU

cpu 占用过高,执行中的时间通常又分为用户态时间 user 和系统态时间 sys。

使用方式:

//on-CPU usersh ngx_on_cpu_u.sh pid //进入结果目录 cd ngx_on_cpu_u //on-CPU kernelsh ngx_on_cpu_k.sh pid //进入结果目录 cd ngx_on_cpu_k //开一个临时端口 8088 python -m SimpleHTTPServer 8088//打开浏览器输入地址127.0.0.1:8088/pid.svg

DEMO:

#include <stdio.h> #include <stdlib.h> void foo3() { } void foo2(){ int i; for(i=0 ; i < 10; i++) foo3(); } void foo1() { int i; for(i = 0; i< 1000; i++) foo3(); } int main(void) { int i; for( i =0; i< 1000000000; i++){ foo1(); foo2(); } }

DEMO 火焰图:

image-20230420093147058

6.4.2 off-CPU

cpu 过低,利用率不高。等待下一轮 CPU,或者等待 I/O、锁、换页等等,其状态可以细分为可执行、匿名换页、睡眠、锁、空闲等状态。

使用方式:

// off-CPU usersh ngx_off_cpu_u.sh pid //进入结果目录cd ngx_off_cpu_u //off-CPU kernelsh ngx_off_cpu_k.sh pid //进入结果目录cd ngx_off_cpu_k //开一个临时端口8088python -m SimpleHTTPServer 8088 //打开浏览器输入地址127.0.0.1:8088/pid.svg

官网 DEMO:

image-20230420093152837

6.5 内存级别火焰图

如果线上程序出现了内存泄漏,并且只在特定的场景才会出现。这个时候我们怎么办呢?有什么好的方式和工具能快速的发现代码的问题呢?同样内存级别火焰图帮你快速分析问题的根源。

使用方式:

sh ngx_on_memory.sh pid //进入结果目录cd ngx_on_memory //开一个临时端口8088python -m SimpleHTTPServer 8088 //打开浏览器输入地址127.0.0.1:8088/pid.svg

官网 DEMO:

image-20230420093159037

6.6 性能回退 - 红蓝差分火焰图

你能快速定位 CPU 性能回退的问题么?如果你的工作环境非常复杂且变化快速,那么使用现有的工具是来定位这类问题是很具有挑战性的。当你花掉数周时间把根因找到时,代码已经又变更了好几轮,新的性能问题又冒了出来。主要可以用到每次构建中,每次上线做对比看,如果损失严重可以立马解决修复。

通过抓取了两张普通的火焰图,然后进行对比,并对差异部分进行标色:红色表示上升,蓝色表示下降。差分火焰图是以当前(“修改后”)的 profile 文件作为基准,形状和大小都保持不变。因此你通过色彩的差异就能够很直观的找到差异部分,且可以看出为什么会有这样的差异。

使用方式:

cd quick_location //抓取代码修改前的profile 1文件perf record -F 99 -p pid -g -- sleep 30perf script > out.stacks1 //抓取代码修改后的profile 2文件perf record -F 99 -p pid -g -- sleep 30perf script > out.stacks2 //生成差分火焰图:./FlameGraph/stackcollapse-perf.pl ../out.stacks1 > out.folded1./FlameGraph/stackcollapse-perf.pl ../out.stacks2 > out.folded2./FlameGraph/difffolded.pl out.folded1 out.folded2 | ./FlameGraph/flamegraph.pl > diff2.svg

DEMO:

//test.c #include <stdio.h> #include <stdlib.h> void foo3() { } void foo2() { int i; for(i=0 ; i < 10; i++) foo3(); } void foo1() { int i; for(i = 0; i< 1000; i++) foo3(); } int main(void) { int i; for( i =0; i< 1000000000; i++){ foo1(); foo2(); } } //test1.c #include <stdio.h> #include <stdlib.h> void foo3() { } void foo2() { int i; for(i=0 ; i < 10; i++) foo3(); } void foo1() { int i; for(i = 0; i< 1000; i++) foo3(); } void add() { int i; for(i = 0; i< 10000; i++) foo3(); } int main(void) { int i; for( i =0; i< 1000000000; i++){ foo1(); foo2(); add(); } }

DEMO 红蓝差分火焰图:

image-20230420093206388

案例分析

7.1 接入层 nginx 集群异常现象

通过监控插件发现在 2017.09.25 19 点 nginx 集群请求流量出现大量的 499,5xx 状态码。并且发现机器 cpu 使用率升高,目前一直持续中。

7.2 分析 nginx 相关指标

a)分析 nginx 请求流量:

image-20230420093212931

结论:

通过上图发现流量并没有突增,反而下降了,跟请求流量突增没关系。

b)分析 nginx 响应时间 image-20230420093218097

结论:

通过上图发现 nginx 的响应时间有增加可能跟 nginx 自身有关系或者跟后端 upstream 响应时间有关系。

c)分析 nginx upstream 响应时间

image-20230420093224500

结论:

通过上图发现 nginx upstream 响应时间有增加,目前猜测可能后端 upstream 响应时间拖住 nginx,导致 nginx 出现请求流量异常。

7.3 分析系统 cpu 情况

a)通过 top 观察系统指标

top

image-20230420093229843

结论:

发现 nginx worker cpu 比较高

b)分析 nginx 进程内部 cpu 情况

perf top -p pid

结论:

发现主要开销在 free,malloc,json 解析上面

7.4 火焰图分析 cpu

a)生成用户态 cpu 火焰图

//on-CPU usersh ngx_on_cpu_u.sh pid //进入结果目录cd ngx_on_cpu_u //开一个临时端口8088python -m SimpleHTTPServer 8088 //打开浏览器输入地址127.0.0.1:8088/pid.svg

image-20230420093235847

结论:

发现代码里面有频繁的解析 json 操作,并且发现这个 json 库性能不高,占用 cpu 挺高。

7.5 案例总结

a) 分析请求流量异常,得出 nginx upstream 后端机器响应时间拉长

b) 分析 nginx 进程 cpu 高,得出 nginx 内部模块代码有耗时的 json 解析以及内存分配回收操作

7.5.1 深入分析

根据以上两点问题分析的结论,我们进一步深入分析。

后端 upstream 响应拉长,最多可能影响 nginx 的处理能力。但是不可能会影响 nginx 内部模块占用过多的 cpu 操作。并且当时占用 cpu 高的模块,是在请求的时候才会走的逻辑。不太可能是 upstram 后端拖住 nginx,从而触发这个 cpu 的耗时操作。

7.5.2 解决方式

遇到这种问题,我们优先解决已知的,并且非常明确的问题。那就是 cpu 高的问题。解决方式先降级关闭占用 cpu 过高的模块,然后进行观察。经过降级关闭该模块 cpu 降下来了,并且 nginx 请求流量也正常了。之所以会影响 upstream 时间拉长,因为 upstream 后端的服务调用的接口可能是个环路再次走回到 nginx。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Gustav

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!