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

2020-07-16 10:05:35
未標題-16.png

業務背景

框架及相應環境

  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檢視進程詳細的執行情況

下載 (3).jpg過程中也沒發現什麼異樣, 並且和top命令的執行結果也基本一致.

vmstat

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

下載 (4).jpg

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

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

watch -d cat /proc/interrupts

下載 (5).jpg

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

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%)的詳細內容,更多請關注TW511.COM其它相關文章!