Perf 工具帮你定位CPU使用率过高案例一则

你的准备


下面我们就以 Nginx + PHP 的 Web 服务为例,来看看当你发现 CPU 使用率过高的问题后,要怎么使用 top 等工具找出异常的进程,又要怎么利用 perf 找出引发性能问题的函数。

・机器配置:4 CPU, 2GB内存

[root@www ~]# cat /proc/cpuinfo | grep 'physical id' | sort | uniq
physical id    : 0
physical id    : 2
physical id    : 4
physical id    : 6

•预先安装 sysstatperfab 等工具

[root@www ~]# yum install httpd-tools perf sysstat -y
Package httpd-tools-2.4.6-93.el7.centos.x86_64 already installed and latest version
Package perf-3.10.0-1127.13.1.el7.x86_64 already installed and latest version
Package sysstat-10.1.5-19.el7.x86_64 already installed and latest version

我先简单介绍一下这次新使用的工具 ab。ab(apache bench)是一个常用的 HTTP 服务性能测试工具,这里用来模拟 Ngnix 的客户端。由于 Nginx 和 PHP 的配置比较麻烦,我把它们打包成了两个 Docker 镜像,这样只需要运行两个容器,就可以得到模拟环境。

你可以看到,其中一台用作Web服务器,来模拟性能问题,另一台用作Web服务器的客 端,来给Web服务增加压力请求。使用两台虚拟机是为了相互隔离,避免"交叉感染”.接下来,我们打开两个终端,分别SSH登录到两台机器上,并安装上面提到的工具。

不过,操作之前,我还想再说一点。这次案例中PHP应用的核心逻辑比较简单,大部分人一眼 就可以看出问题,但你要知道,实际生产环境中的源码就复杂多了。

所以,我希望你在按照步骤操作之前,先不要查看源码(避免先入为主),而是把它当成一个黑盒来分析。这样你可以更好地理解整个解决思路,怎么从系统的资源使用问题出发,分析出瓶颈所在的应用以及瓶颈在应用中的大概位置。

 

操作和分析


首先,在第一个终端执行下面的命令来运行 Nginx 和 PHP 应用:

[root@localhost ~]#  docker run --name nginx -p 10000:80 -itd feisky/nginx
464c559c39caa60d8669487b8fb817eedbe6c75af030afd87550c08ff2dac495
[root@localhost ~]# docker run --name phpfpm -itd --network container:nginx feisky/php-fpm
310458478b2a12effbfa3e8bdb65a796b62a81cde984f6efcc785ba18d1080c9

[root@localhost ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                   NAMES
310458478b2a        feisky/php-fpm      "php-fpm -F --pid /o…"   About a minute ago   Up About a minute                           phpfpm
464c559c39ca        feisky/nginx        "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:10000->80/tcp   nginx

 在第一个终端使用curl访问http://[VM1IP]:10000,确认Nginx已正常启动,你应该可以看到It works!的响应。

[root@localhost ~]# curl 192.168.179.99:10000
It works!

接着,我们来测试一下这个Nginx服务的性能。在第二个终端运行下面的ab命令:

#并发10个请求测试Nginx性能,总共测试100个请求

[root@localhost ~]# ab -c 10 -n 10000 http://192.168.179.99:10000/
.......................
Requests per second:    7.25 [#/sec] (mean)
Time per request:       1379.778 [ms] (mean)
.......................

ab的输出结果我们可以看到,Nginx能承受的每秒平均请求数只有7.25。你一定在吐槽, 这也太差了吧。那到底是哪里出了问题呢?我们用toppidstat再来观察下。

这次,我们在第二个终端,将测试的请求总数增加到10000。这样当你在第一个终端使用性能分析工具时,Nginx的压力还是继续。

[root@localhost ~]# ab -c 10 -n 10000 http://192.168.179.99:10000/

接着,回到第一个终端运行top命令,并按下数字1 ,切换到每个CPU的使用率:

[root@localhost ~]# top
top - 13:23:33 up 1 day, 13:44,  2 users,  load average: 2.26, 0.58, 0.23
Tasks: 113 total,   6 running, 107 sleeping,   0 stopped,   0 zombie
%Cpu0  : 99.7 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 99.7 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1765672 total,   145092 free,   176300 used,  1444280 buff/cache
KiB Swap:   524284 total,   477760 free,    46524 used.  1287080 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                       
 28847 bin       20   0  336684   7352   1616 R  41.2  0.4   0:13.14 php-fpm                                       
 28849 bin       20   0  336684   7352   1616 R  40.9  0.4   0:12.95 php-fpm                                       
 28848 bin       20   0  336684   7352   1616 R  38.9  0.4   0:13.27 php-fpm                                       
 28850 bin       20   0  336684   7356   1620 R  38.9  0.4   0:12.50 php-fpm                                       
 28846 bin       20   0  336684   7356   1620 R  37.9  0.4   0:12.53 php-fpm                                       
 28845 root      20   0   58776   3000   2240 S   1.0  0.2   0:00.13 ab                                            
 17478 root      20   0  627036  70424  27348 S   0.7  4.0   2:44.93 dockerd                                       
 28674 101       20   0   33092   2116    776 S   0.3  0.1   0:00.23 nginx                                         
 28851 root      20   0  157584   2140   1508 R   0.3  0.1   0:00.07 top   

这里可以看到,系统中有几个php-fpm进程的CPU使用率加起来接近200%,而每个CPU的用户使用率(us)也已经超过了 99%,接近饱和。这样我们就可以确认,正是用户空间的 php-fpm进程,导致CPU使用率骤升。

那再往下走,怎么知道是php-fpm的哪个函数导致了 CPU使用率升高呢?我们来用perf分析 一下。在第一个终端运行下面的perf命令:

# -g开启调用关系分析,-p指定php-fpm的进程号21515
[root@localhost ~]# perf top -g -p 21515

按方向键切换到php-fpm,再按下回车键展开php-fpm的调用关系,你会发现,调用关系最 终到了 sqrt和emalloc看来,我们需要从这两个函数入手了。


Samples: 39K of event 'cpu-clock', 4000 Hz, Event count (approx.): 5609807665 lost: 0/0 drop: 0/0                                                                                                                      
  Shared Object     Symbol                                                                                                                                                                                             
- php-fpm           [.] execute_ex                                                                                                                                                                                       
 - 21.39% execute_ex                                                                                                                                                                                                      
   - 13.08% ZEND_DO_FCALL_SPEC_CONST_HANDLER                                                                                                                                                                                
     - 15.10% zend_do_fcall_common_helper_SPEC                                                                                                                                                                                
         + 4.28% _emalloc                                                                                                                                                                                                      
         + 4.17% zif_sqrt                                                                                                                                                                                                      
         + 2.95% zend_vm_stack_clear_multiple                                                                                                                                                                                    
         1.57% __sqrt            

我们拷贝出 Nginx 应用的源码,看看是不是调用了这两个函数:

从容器phpfpm中将PHP源码拷贝出来
[root@localhost ~]# docker cp phpfpm:/app .
[root@localhost ~]# ls
anaconda-ks.cfg  app


# 使用grep查找函数调用
[root@localhost ~]# grep sqrt -r app/
app/index.php:  $x += sqrt($x);

OK,原来只有sqrt函数在app/index.php文件中调用了。那最后一步,我们就该看看这个文件的源码了: 

[root@localhost ~]# cat app/index.php 
<?php
// test only.
$x = 0.0001;
for ($i = 0; $i <= 1000000; $i++) {
  $x += sqrt($x);
}

echo "It works!"

修改代码将for循环去掉 ,在跑一下

[root@localhost ~]# docker rm -f phpfpm
phpfpm
[root@localhost ~]# docker run --name phpfpm -itd --network container:nginx -v /root/index.php:/app/index.php feisky/php-fpm
4d8ba484bb910c813c5552b663e058edd0ed506890de30df0a17292d5b9fa5a2
[root@localhost ~]# docker exec -it phpfpm /bin/bash
root@464c559c39ca:/app# ls
404.html  index.php  ok.php  phpinfo.php
root@464c559c39ca:/app# cat index.php 
<?php
// test only.
echo "It works!"
?>


[root@localhost ~]# ab -c 10 -n 10000 http://192.168.179.99:10000/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Requests per second:    3008.24 [#/sec] (mean)
Time per request:       3.324 [ms] (mean)

从这里你可以发现,现在每秒的平均请求数,已经从原来的7.25变成了3008.24。

你看这么很傻的小问题却会极大的影响性能,并且査找起来也并不容易吧。当然找到问题后,解决方法就简单多了,删除测试代码就可以了。

 

小结


CPU 使用率是最直观和最常用的系统性能指标,更是我们在排查性能问题时,通常会关注的第一个指标。所以我们更要熟悉它的含义,尤其要弄清楚用户(%user)、Nice(%nice)、系统(%system) 、等待 I/O(%iowait) 、中断(%irq)以及软中断(%softirq)这几种不同 CPU 的使用率。比如说:

  • 用户 CPU 和 Nice CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题
  • 系统 CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题
  • I/O 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题
  • 软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序

碰到 CPU 使用率升高的问题,你可以借助 top、pidstat 等工具,确认引发 CPU 性能问题的来源,再使用 perf 等工具,排查出引起性能问题的具体函数。 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值