記一次 Laravel 應用效能調優經歷

weixin_34320159發表於2017-08-30

這是一份事後的總結。在經歷了調優過程踩的很多坑之後,我們最終完善並實施了初步的效能測試方案,通過真實的測試資料歸納出了 Laravel 開發過程中的一些實踐技巧。

0x00 源起

最近有同事反饋 Laravel 寫的應用程式響應有點慢、20幾個併發把 CPU 跑滿... 為了解決慢的問題,甚至一部分介面用 nodejs 來寫。

而我的第一反應是一個流行的框架怎麼可能會有這麼不堪?一定是使用上哪裡出現了問題。為了一探究竟,於是開啟了這次 Laravel 應用效能調優之旅。

0x01 調優技巧

這次效能測試方案中用到的優化技巧主要基於 Laravel 框架本身及其提供的工具。

  1. 關閉應用debug app.debug=false
  2. 快取配置資訊 php artisan config:cache
  3. 快取路由資訊 php artisan router:cache
  4. 類對映載入優化 php artisan optimize
  5. 自動載入優化 composer dumpautoload
  6. 根據需要只載入必要的中介軟體
  7. 使用即時編譯器(JIT),如:HHVM、OPcache
  8. 使用 PHP 7.x

除了以上優化技巧之外,還有很多編碼上的實踐可以提升 Laravel 應用效能,在本文中暫時不會做說明。(也可以關注我的後續文章)

1. 關閉應用 debug

開啟應用根目錄下的 .env 檔案,把 debug 設定為 false。

APP_DEBUG=false

2. 快取配置資訊

php artisan config:cache

執行以上命令可以把 config 資料夾裡所有配置資訊合併到一個 bootstrap/cache/config.php 檔案中,減少執行時載入檔案的數量。

php artisan config:clear

執行以上命令可以清除配置資訊的快取,也就是刪除 bootstrap/cache/config.php 檔案

3. 快取路由資訊

php artisan route:cache

執行以上命令會生成檔案 bootstrap/cache/routes.php。路由快取可以有效的提高路由器的註冊效率,在大型應用程式中效果越加明顯。

php artisan route:clear

執行以上命令會清除路由快取,也就是刪除 bootstrap/cache/routes.php 檔案。

4. 類對映載入優化

php artisan optimize --force

執行以上命令能夠把常用載入的類合併到一個檔案中,通過減少檔案的載入來提高執行效率。這個命令會生成 bootstrap/cache/compiled.phpbootstrap/cache/services.json 兩個檔案。

通過修改 config/compile.php 檔案可以新增要合併的類。

在生產環境中不需要指定 --force 引數檔案也可以自動生成。

php artisan clear-compiled

執行以上命令會清除類對映載入優化,也就是刪除 bootstrap/cache/compiled.phpbootstrap/cache/services.json 兩個檔案。

5. 自動載入優化

composer dumpautoload -o

Laravel 應用程式是使用 composer 來構建的。這個命令會把 PSR-0 和 PSR-4 轉換為一個類對映表來提高類的載入速度。

注意:php artisan optimize --force 命令裡已經做了這個操作。

6. 根據需要只載入必要的中介軟體

Laravel 應用程式內建了並開啟了很多的中介軟體。每一個 Laravel 的請求都會載入相關的中介軟體、產生各種資料。在 app/Http/Kernel.php 中註釋掉不需要的中介軟體(如 session 支援)可以極大的提升效能。

7. 使用即時編譯器

HHVM 和 OPcache 都能輕輕鬆鬆的讓你的應用程式在不用做任何修改的情況下,直接提高 50% 或者更高的效能。

8. 使用 PHP 7.x

只能說 PHP 7.x 比起之前的版本在效能上有了極大的提升。

嗯,限於你的真實企業環境,這個也許很長時間內改變不了,算我沒說。

0x02 測試方案

我們使用簡單的 Apache ab 命令僅對應用入口檔案進行測試,並記錄和分析資料。

  1. 僅對應用的入口檔案 index.php 進行測試,訪問 “/” 或者 “/index.php” 返回框架的歡迎頁面。更全面的效能測試需要針對應用的更多介面進行測試。
  2. 使用 Apache ab 命令。ab -t 10 -c 10 {url}。該命令表示對 url 同時發起 10 個請求,並持續 10 秒鐘。命令中具體的引數設定需要根據要測試的伺服器效能進行選擇。
  3. 為了避免機器波動導致的資料錯誤,每種測試條件會執行多次 ab 命令,並記錄命令執行結果,重點關注每秒處理的請求數及請求響應時間,分析並剔除異常值。
  4. 每次對測試條件進行了調整,需要在瀏覽器上對歡迎頁進行訪問,確保沒有因為測試條件修改而訪問出錯。如果頁面訪問出錯會導致測試結果錯誤。

伺服器環境說明

所有脫離具體環境的測試資料都沒有意義,並且只有在相近的條件下才可以進行比較。

  1. 這套環境執行在 Mac 上,記憶體 8G,處理器 2.8GHz,SSD 硬碟。
  2. 測試伺服器是使用 Homestead 搭建的。虛擬機器配置為單核 CPU、2G 記憶體。
  3. 伺服器 PHP 版本為 7.1,未特殊說明,則標識開啟了 OPcache。
  4. 測試的 Laravel 應用程式採用 5.2 版本編寫。app\Http\routes.php 中定義了 85 個路由。
  5. 測試過程中除了虛擬機器、終端及固定的瀏覽器視窗外,沒有會影響機器的程式執行。

以上的資料,大家在自己進行測試時可以參考。

0x03 測試過程及資料

1. 未做任何優化

1.1 操作

  • 按照以下檢查項執行相應的操作。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

基礎檢查項

  • .env 檔案中 APP_DEBUG=true
  • 不存在 bootstrap/cache/config.php
  • 不存在 bootstrap/cache/routes.php
  • 不存在 bootstrap/cache/compiled.phpbootstrap/cache/services.json
  • app/Http/Kernel.php 中開啟了大部分的中介軟體
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問

1.2 資料記錄

7098059-1fb223d0a5aa3532
1-未開啟優化

2. 關閉應用debug

2.1 操作

  • 在步驟 1 基礎上修改 .env 檔案中 APP_DEBUG=false
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

2.2 資料記錄

7098059-b392d6083f1569b7
2-關閉應用debug

2.3 對比結果

與步驟 1 結果比較發現:關閉應用 debug 之後,每秒處理請求數從 26-34 上升到 33-35,請求響應時間從 大部分 300ms 以上下降到 290ms 左右,效果不太明顯,但確實有一定的提升。

注意:這部分與應用中的日誌等使用情況有比較大的關聯。

3. 開啟快取配置資訊

3.1 操作

  • 在步驟 2 基礎上,執行 php artisan config:cache,確認生成 bootstrap/cache/config.php
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

3.2 資料記錄

7098059-b0803720a86b38e8
3-快取配置資訊

3.3 對比結果

與步驟 2 結果比較發現:開啟配置資訊快取之後,每秒處理請求數從 33-35 上升到 36-38,請求響應時間從 290ms 左右下降到 260ms 左右,效果不太明顯,但確實有一定的提升。

4. 開啟快取路由資訊

4.1 操作

  • 在步驟 3 基礎上,執行 php artisan route:cache,確認生成 bootstrap/cache/routes.php
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

4.2 資料記錄

7098059-e89b2f861cad9579
4-快取路由資訊

4.3 對比結果

與步驟 3 結果比較發現:開啟路由資訊快取之後,每秒處理請求數從 36-38 上升到 60 左右,請求響應時間從 260ms 下降到 160ms 左右,效果顯著,從 TPS 看,提升了 70%。

5. 刪除不必要的中介軟體

5.1 操作

  • 在步驟 4 基礎上,註釋掉不必要的中介軟體程式碼。
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php
7098059-0c3dd7c267aac22d
註釋掉中介軟體程式碼

注意:這次測試中我註釋掉了所有的中介軟體。實際情況中應該儘量只留下必要的中介軟體。

5.2 資料記錄

7098059-c553d69abc04d4ec
5-刪除不必要的中介軟體

5.3 對比結果

與步驟 4 結果比較發現:刪除了不必要的中介軟體之後,每秒處理請求數從 60 左右上升到 90 左右,請求響應時間從 160ms 下降到 110ms 左右,效果非常明顯,從 TPS 看,提升了 50%。

6. 開啟類對映載入優化

6.1 操作

  • 在步驟 5 基礎上,執行 php artisan optimize --force,確認生成 bootstrap/cache/compiled.phpbootstrap/cache/services.json
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

6.2 資料記錄

7098059-6258862bb59944b5
6-類對映載入優化

6.3 對比結果

與步驟 5 結果比較發現:做了類對映載入優化之後,每秒處理請求數從 90 上升到 110,請求響應時間從 110ms 下降到 100ms 以下,效果還是比較明顯的。

7. 關閉 OPcache

7.1 操作

  • 在步驟 6 基礎上,關閉 PHP 的 OPcache,並重啟伺服器。通過 phpinfo() 的 Zend OPcache 確認 OPcache 已經關閉。
  • 瀏覽器訪問 Laravel 應用程式歡迎頁確保正常訪問。
  • 執行 ab -t 10 -c 10 http://myurl.com/index.php

7.2 資料記錄

7098059-8b0e223ce8d62c1f
7-關閉了OPcache

7.3 對比結果

與步驟 6 結果比較發現:關閉 OPcache 之後,每秒處理請求數從 110 下降到 15,請求響應時間從 100ms 以下上升到 650ms 以上。開啟與關閉 OPcache,資料上竟有幾倍的差別。

此後,我重新開啟了 PHP 的 OPcache,資料恢復到步驟 6 水平。

0x04 踩過的坑

1. [LogicException] Unable to prepare route [/] for serialization. Uses Closure.

在執行 php artisan route:cache 命令時報這個錯誤。

原因:路由檔案中處理“/”時使用了閉包的方式。要執行該命令,路由的具體實現不能使用閉包方式。

修改方案:將路由的具體實現放到控制器中來實現。

2. [Exception] Serialization of 'Closure' is not allowed.

在執行 php artisan route:cache 命令時報這個錯誤。

原因:路由檔案中定義了重複的路由。

修改方案:排查路由檔案中的重複路由並修改。尤其要注意 resource 方法很可能導致與其方法重複。

3. [RuntimeException] Invalid filename provided.

在執行 php artisan optimize --force 命名時報這個錯誤。

原因:在載入需要編譯的類時沒有找到相應的檔案。5.2 版本的 vendor/laravel/framework/src/Illuminate/Foundation/Console/Optimize/config.php 中定義了要編譯的檔案路徑,但不知道為什麼 /vendor/laravel/framework/src/Illuminate/Database/Eloquent/ActiveRecords.php 沒有找到,所以報了這個錯誤。

修改方案:暫時註釋掉了以上 config.php 中的 ../ActiveRecords.php 一行。

4. InvalidArgumentException in FileViewFinder.php line 137: View [welcome] not found.

在執行 php artisan config:cache 之後,瀏覽器上訪問 Laravel 應用程式歡迎頁報這個錯誤。

原因:Laravel 應用程式伺服器是通過 Homestead 在虛擬機器上搭建的。而這個命令我是在虛擬機器之外執行的,導致生成的 config.php 中的路徑是本機路徑,而不是虛擬機器上的路徑。所以無法找到檢視檔案。

修改方案:ssh 到虛擬機器內部執行該命令。

0x04 實踐技巧

坑也踩了,測試也做過了。這裡針對這次經歷做個實踐技巧的簡單總結。

1. 有效的 Laravel 應用程式優化技巧

  1. 關閉應用debug app.debug=false
  2. 快取配置資訊 php artisan config:cache
  3. 快取路由資訊 php artisan router:cache
  4. 類對映載入優化 php artisan optimize(包含自動載入優化 composer dumpautoload
  5. 根據需要只載入必要的中介軟體
  6. 使用即時編譯器(JIT),如:HHVM、OPcache

2. 編寫程式碼時注意事項

  1. 路由的具體實現放到控制器中。
  2. 不定義重複的路由,尤其注意 resouce 方法。
  3. 弄清各中介軟體的作用,刪除不必要的中介軟體引用。

0x06 下一步

以上的調優技巧及編碼注意事項主要針對框架本身,在真正的業務邏輯編碼中有很多具體的優化技巧,在此沒有討論。

後續的優化重點將會放在具體編碼實踐上:

  1. 使用 Memcached 來儲存會話 config/session.php
  2. 使用專業的快取驅動器
  3. 資料庫請求優化
  4. 為資料集書寫快取邏輯
  5. 前端資源合併 Elixir

0x07 寫在最後

網上看到很多框架效能對比的文章與爭論,也看到很多簡單貼出了資料。這些都不足以窺探真實的情況,所以有了我們這次的實踐,並在過程中做了詳實的記錄。在各位讀者實踐過程中提供參考、比較、反思之用。對於這次實踐有疑問的讀者,也歡迎提出問題和意見。

不多說了,要學習更多技術乾貨,請關注微信公眾號:up2048。

- EOF -

推薦閱讀

相關文章