运维大佬经验分享:如何找出让CPU使用率达到100%的应用(2)

该系列文章共计两篇,本文为第二篇!

案例

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

实战准备

机器配置:2CPU,8GB内存

预先安装docker、sysstat、perf、ab等工具,如apt install docker. io sysstat linux-tools

common apache 2-utils

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

注意,这个案例要用到两台虚拟机,如下:

IP(VM1):192.168.139.99

IP(VM2):192.168.139.98

你可以看到,其中一台用作Web服务器,来模拟性能问题;另一台用作Web服务器的客户端,来给Web服务增加压力请求。使用两台虚拟机是为了相互隔离,避免"交叉感染"。

接下来,我们打开两个终端,分别SSH登录到两台机器上,并安装上面提到的工具

还是同样的"配方"。下面的所有命令,都默认假设以root用户运行,如果你是普通用户身份登陆系统,一定要先运行sudo su root命令切换到root用户。到这里,准备工作就完成了。

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

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

操作和分析

接下来,我们正式进入操作环节。

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

docker run --name nginx -p 10000:80 -itd feisky/nginx

docker run --name phpfpm -itd --network container:nginx feisky/php-fpm

然后,在第二个终端使用cur访问http://[vm1的IP]:10000确认Nginx已正常启动。你应该可以看到"It works!"的响应。

# 192.168.139.99 是第一台虚拟机的 IP 地址

$ curl http:// 192.168.139.99:10000/

It works!

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

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

$ ab -c 10 -n 100 http://192.168.139.99:10000/

This is ApacheBench, Version 2.3 <$Revision: 1706008 $>

Copyright 1996 Adam Twiss, Zeus Technology Ltd,

...

Requests per second: 11.63 [#/sec] (mean)

Time per request: 859.942 [ms] (mean)

...

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

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

继续在第二个终端,运行ab命:

ab -c 10 -n 10000 http://192.168.139.99:10000/

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

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

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

# -g 开启调用关系分析,-p 指定 php-fpm 的进程号 21515

$ perf top -g -p 21515

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

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

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

cat app/index.php

<?php

// test only.

$x = 0.0001;

for ($i = 0; $i <= 1000000; $i++) {

$x += sqrt($x);

}

echo "It works!"

呀,有没有发现问题在哪里呢?我想你要笑话我了,居然犯了一个这么傻的错误,测试代码没删就直接发布应用了。为了方便你验证优化后的效果,我把修复后的应用也打包成了一个 Docker镜像,你可以在第一个终端中执行下面的命令来运行它:

# 停止原来的应用

$ docker rm -f nginx phpfpm

# 运行优化后的应用

$ docker run --name nginx -p 10000:80 -itd feisky/nginx:cpu-fix

$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:cpu-fix

接着,到第二个终端来验证一下修复后的效果。首先Ctrl+C停止之前的ab命令后,再运行下面的命令:

ab -c 10 -n 10000 http://10.240.0.5:10000/

...

Complete requests: 10000

Failed requests: 0

Total transferred: 1720000 bytes

HTML transferred: 90000 bytes

Requests per second: 2237.04 [#/sec] (mean)

Time per request: 4.470 [ms] (mean)

Time per request: 0.447 [ms] (mean, across all concurrent requests)

Transfer rate: 375.75 [Kbytes/sec] received

...

从这里我们可以看出,现在每秒的平均请求数,已经从原来的11变成了2237。

就这一个很小的问题,却影响了这么大的性能,而且查起来也不容易。当然,找到问题后,解决的办法就简单多了,删除测试代码即可!

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();