首頁  >  文章  >  後端開發  >  PHP並發效能調優實戰(效能提升104%)

PHP並發效能調優實戰(效能提升104%)

步履不停
步履不停原創
2019-06-10 10:29:598523瀏覽

PHP並發效能調優實戰(效能提升104%)

業務背景

##框架及對應環境

  1. laravel5.7, mysql5.7, redis5, nginx1.15

  2. centos 7.5 bbr

  3. #docker, docker-compose

  4. 阿里雲4C和8G

問題背景

#php已經開啟opcache, laravel也運行了optimize命令進行優化, composer也進行過dump-autoload命令.

首先需要聲明的是, 系統的環境中是一定有小問題的(沒有問題也不可能能夠提升如此大的性能), 但是這些問題, 如果不通過使用合適的工具, 可能一輩子也發現不出來.

本文關注的就是如何發現這些問題, 以及發現問題的思路.

我們先找到系統中一個合適的API或函數, 用來放大問題.

這個api設計之初是給nginx負載均衡做健康檢查的. 使用ab -n 100000 -c 1000 進行壓測, 發現qps只能到140個每秒.

我們知道Laravel的性能是出了名的不好, 但是也不至於到這個程度, 從api的編寫來看不應該這麼低. 所以決定一探究竟.

 public function getActivateStatus()
    {
        try {
            $result = \DB::select('select 1');
            $key = 1;
            if ($result[0]->$key !== 1) {
                throw new \Exception("mysql 检查失败");
            }
        } catch (\Exception $exception) {
            \Log::critical("数据库连接失败: {$exception->getMessage()}", $exception->getTrace());
            return \response(null, 500);
        }
        try {
            Cache::getRedis()->connection()->exists("1");
        } catch (\Exception $exception) {
            \Log::critical("缓存连接失败: {$exception->getMessage()}", $exception->getTrace());
            return \response(null, 500);
        }
        return \response(null, 204);
    }

問題表現以及排查思路

top

top命令發現系統CPU佔用100% 其中用戶態佔80%, 核心態佔20%, 看起來沒什麼大問題. 有一個地方看起來很奇怪, top指令的運行結果

下载 (2).jpg

就是有一部分php-fpm行程處在Sleep狀態, 但CPU佔用還是達到了近30%. 當一個行程處於Sleep狀態的時候, 任然佔用了不少CPU, 先不要懷疑是不是進程的問題, 我們看一下Ttop指令的man page.

%CPU -- CPU usage

The task's share of the elapsed CPU time since the last screen update, expressed as a percentage of total CPU time.

大致意思是這個佔用是最後一次螢幕刷新的時候, 進程CPU的佔用. 由於top命令收集信息的時候, 可能linux把這個進程強制調度了( 比如用於top收集進程信息), 所以在這一瞬間(屏幕刷新的這一瞬間)某些php-fpm行程處於sleep狀態, 可以理解, 所以應該不是php-fpm的問題.

#pidstat##先選出一個php-fpm行程, 然後使用pidstat查看進程詳細的運行情況

過程中也沒發現什麼異樣, 並且和top命令的運行結果也基本一致.

下载 (3).jpg##vmstat

保持壓測壓力, 運行vmstate查看, 除了context switch (上下文切換)有點高之外, 並沒有看到太多異常. 由於我們使用的docker, redis, mysql都運行在同一台機器上, 7000左右的CS還是一個合理的範圍, 但是這個IN(中斷)就有點太高了, 達到了1.4萬左右. 一定有什麼東西觸發中斷.

我們知道中斷有硬中斷和軟中斷, 硬中斷是由網卡, 滑鼠等硬體發出中斷訊號, cpu馬上停下在做的事情,處理中斷訊號. 軟中斷是由作業系統發出的, 常用於進程的強制調度.下载 (4).jpg

不管是vmstat還是pidstat都只是新能探測工具, 我們無法看到具體的中斷是由誰發出的. 我們透過/proc/interrupts 這個只讀檔案中讀取系統的中斷訊息, 取得到底是什麼導致的中斷升高. 透過watch -d指令, 判斷變化最頻繁的中斷.

watch -d cat /proc/interrupts

我們發現其中Rescheduling interrupts變化的最快, 這個是重調度中斷(RES),這個中斷類型表示,喚醒空閒狀態的CPU 來調度新的任務運行。這是多處理器系統(SMP)中,調度器用來分散任務到不同 CPU的機制,通常也稱為處理器間中斷(Inter-Processor Interrupts,IPI)。 結合vmstat中的命令, 我們可以確定造成qps不高的原因之一是過多的進程爭搶CPU導致的, 我們現在還不能確定具體是什麼, 所以還需要進一步的排查.下载 (5).jpg

strace

strace可以查看系統呼叫, 我們知道, 當使用系統呼叫的時候, 系統陷入核心態, 這個過程是會產生軟中斷的, 透過查看php-fpm的系統呼叫, 驗證我們的猜想

下载 (6).jpg果然, 发现大量的stat系统调用, 我们猜想, 是opcache在检查文件是否过期导致的. 我们通过修改opcache的配置, 让opcache更少的检查文件timestamp, 减少这种系统调用

 opcache.validate_timestamps="60"
    opcache.revalidate_freq="0"

再次执行ab命令进行压测

下载 (7).jpg果然qps直接涨到了205, 提升非常明显, 有接近 46% 的提升

perf

现在任然不满足这个性能, 希望在更多地方找到突破口. 通过

perf record -g
perf report -g

看到系统的分析报告

下载 (8).jpg

我们看到, 好像这里面有太多tcp建立相关的系统调用(具体是不是我还不清楚, 请大神指正, 但是看到send, ip, tcp啥的我就怀疑可能是tcp/ip相关的问题).

我们怀疑两种情况

  1. 与mysql, redis重复大量的建立TCP连接, 消耗资源

  2. 大量请求带来的tcp连接

先说第一个, 经过检查, 发现数据库连接使用了php-fpm的连接池, 但是redis连接没有, redis用的predis, 这个是一个纯PHP实现, 性能不高, 换成了phpredis:

打开laravel的config/database.php文件, 修改redis的driver为phpredis, 确保本机已安装php的redis扩展. 另外由于Laravel自己封装了一个Redis门面, 而恰好redis扩展带来的对象名也叫Redis. 所以需要修改Laravel的Redis门面为其他名字, 如RedisL5.

再次进行压测

下载 (9).jpg

达到了喜人的286qps, 虽然和其他主打高性能的框架或者原生php比, 还有很高的提升空间(比如Swoole), 但是最终达到了104%的提升, 还是很有意义的

总结

  1. 我们通过top, 发现系统CPU占用高, 且发现确实是php-fpm进程占用了CPU资源, 判断系统瓶颈来自于PHP.

  2. 接着我们通过pidstat, vmstat发现压测过程中, 出现了大量的系统中断, 并通过 watch -d cat /proc/interrupts 发现主要的中断来自于重调度中断(RES)

  3. 通过strace查看具体的系统调用, 发现大量的系统调用来自于stat, 猜测可能是opcache频繁的检查时间戳, 判断文件修改. 通过修改配置项, 达到了46%的性能提升

  4. 最后再通过perf, 查看函数调用栈, 分析得到, 可能是大量的与redis的TCP连接带来不必要的资源消耗. 通过安装redis扩展, 以及使用phpredis来驱动Laravel的redis缓存, 提升性能, 达到了又一次近50%的性能提升.

  5. 最终我们完成了我们的性能提升104%的目标

推荐教程:网站高并发架设基础教程

以上是PHP並發效能調優實戰(效能提升104%)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn