記一次 Laravel 應用性能調優經歷
這是一份事后的總結。在經歷了調優過程踩的很多坑之后,我們最終完善并實施了初步的性能測試方案,通過真實的測試數據歸納出了 Laravel 開發過程中的一些實踐技巧。
0x00 源起
最近有同事反饋 Laravel 寫的應用程序響應有點慢、20幾個并發把 CPU 跑滿... 為了解決慢的問題,甚至一部分接口用 nodejs 來寫。
而我的第一反應是一個流行的框架怎么可能會有這么不堪?一定是使用上哪里出現了問題。為了一探究竟,于是開啟了這次 Laravel 應用性能調優之旅。
0x01 調優技巧
這次性能測試方案中用到的優化技巧主要基于 Laravel 框架本身及其提供的工具。
- 關閉應用debug app.debug=false
- 緩存配置信息 php artisan config:cache
- 緩存路由信息 php artisan router:cache
- 類映射加載優化 php artisan optimize
- 自動加載優化 composer dumpautoload
- 根據需要只加載必要的中間件
- 使用即時編譯器(JIT),如:HHVM、OPcache
- 使用 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.php 和 bootstrap/cache/services.json 兩個文件。
通過修改 config/compile.php 文件可以添加要合并的類。
在生產環境中不需要指定 --force 參數文件也可以自動生成。
php artisan clear-compiled
運行以上命令會清除類映射加載優化,也就是刪除 bootstrap/cache/compiled.php 和 bootstrap/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 命令僅對應用入口文件進行測試,并記錄和分析數據。
- 僅對應用的入口文件 index.php 進行測試,訪問 “/” 或者 “/index.php” 返回框架的歡迎頁面。更全面的性能測試需要針對應用的更多接口進行測試。
- 使用 Apache ab 命令。 ab -t 10 -c 10 {url} 。該命令表示對 url 同時發起 10 個請求,并持續 10 秒鐘。命令中具體的參數設置需要根據要測試的服務器性能進行選擇。
- 為了避免機器波動導致的數據錯誤,每種測試條件會執行多次 ab 命令,并記錄命令執行結果,重點關注每秒處理的請求數及請求響應時間,分析并剔除異常值。
- 每次對測試條件進行了調整,需要在瀏覽器上對歡迎頁進行訪問,確保沒有因為測試條件修改而訪問出錯。如果頁面訪問出錯會導致測試結果錯誤。
服務器環境說明
所有脫離具體環境的測試數據都沒有意義,并且只有在相近的條件下才可以進行比較。
- 這套環境運行在 Mac 上,內存 8G,處理器 2.8GHz,SSD 硬盤。
- 測試服務器是使用 Homestead 搭建的。虛擬機配置為單核 CPU、2G 內存。
- 服務器 PHP 版本為 7.1,未特殊說明,則標識開啟了 OPcache。
- 測試的 Laravel 應用程序采用 5.2 版本編寫。 app\Http\routes.php 中定義了 85 個路由。
- 測試過程中除了虛擬機、終端及固定的瀏覽器窗口外,沒有會影響機器的程序運行。
以上的數據,大家在自己進行測試時可以參考。
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.php 和 bootstrap/cache/services.json
- app/Http/Kernel.php 中開啟了大部分的中間件
- 瀏覽器訪問 Laravel 應用程序歡迎頁確保正常訪問
1.2 數據記錄

2. 關閉應用debug
2.1 操作
- 在步驟 1 基礎上修改 .env 文件中 APP_DEBUG=false 。
- 瀏覽器訪問 Laravel 應用程序歡迎頁確保正常訪問。
- 運行 ab -t 10 -c 10 http://myurl.com/index.php 。
2.2 數據記錄

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 數據記錄

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 數據記錄

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 。

注意:這次測試中我注釋掉了所有的中間件。實際情況中應該盡量只留下必要的中間件。
5.2 數據記錄

5.3 對比結果
與步驟 4 結果比較發現:刪除了不必要的中間件之后,每秒處理請求數從 60 左右上升到 90 左右,請求響應時間從 160ms 下降到 110ms 左右, 效果非常明顯,從 TPS 看,提升了 50%。
6. 開啟類映射加載優化
6.1 操作
- 在步驟 5 基礎上,運行 php artisan optimize --force ,確認生成 bootstrap/cache/compiled.php 和 bootstrap/cache/services.json 。
- 瀏覽器訪問 Laravel 應用程序歡迎頁確保正常訪問。
- 運行 ab -t 10 -c 10 http://myurl.com/index.php 。
6.2 數據記錄

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 數據記錄

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 應用程序優化技巧
- 關閉應用debug app.debug=false
- 緩存配置信息 php artisan config:cache
- 緩存路由信息 php artisan router:cache
- 類映射加載優化 php artisan optimize (包含自動加載優化 composer dumpautoload )
- 根據需要只加載必要的中間件
- 使用即時編譯器(JIT),如:HHVM、OPcache
2. 編寫代碼時注意事項
- 路由的具體實現放到控制器中。
- 不定義重復的路由,尤其注意 resouce 方法。
- 弄清各中間件的作用,刪除不必要的中間件引用。
0x06 下一步
以上的調優技巧及編碼注意事項主要針對框架本身,在真正的業務邏輯編碼中有很多具體的優化技巧,在此沒有討論。
后續的優化重點將會放在具體編碼實踐上:
- 使用 Memcached 來存儲會話 config/session.php
- 使用專業的緩存驅動器
- 數據庫請求優化
- 為數據集書寫緩存邏輯
- 前端資源合并 Elixir
0x07 寫在最后
網上看到很多框架性能對比的文章與爭論,也看到很多簡單貼出了數據。這些都不足以窺探真實的情況,所以有了我們這次的實踐,并在過程中做了詳實的記錄。在各位讀者實踐過程中提供參考、比較、反思之用。對于這次實踐有疑問的讀者,也歡迎提出問題和意見。
不多說了,要學習更多技術干貨,請關注微信公眾號:up2048。
- EOF -
來自:https://segmentfault.com/a/1190000011569012