PHP配置優化《MordenPHP》
PHP配置優化
來自MordenPHP
PHP-FPM配置優化
全域性配置
在Ubuntu中PHP-FPM的主配置檔案時 /etc/php7/fpm/php-fpm.conf 在CentOS中PHP-FPM的主配置檔案時/etc/php-fpm.conf.
這兩個設定的作用是, 如果指定的一段時間內有指定個數程序失效了. 讓PHP-FPM主程序重啟.這是PHP-FPM程序的基本安全保證,能解決簡單的問題. 但是不能解決由於拙劣的PHP程式碼引起的重大問題.
emergency_restart_threshold = 10 在指定一段時間內,如果失效的PHP-FPM子程序數超過這個值, PHP-FPM主程序就會優雅的重啟. emergency_restart_interval = 1m 設定emergency_restart_threshold設定採用的時間跨度 更多全域性設定的詳細資訊檢視官方手冊
配置程序池
PHP-FPM配置檔案其餘的內容時一個名為Pool Definitions的區域. 這個區域的配置用於設定每個PHP-FPM程序池. PHP-FPM程序池中是一些列相關的PHP子程序. 通過一個PHP應用有自己的一個PHP-FPM程序池.
在Ubuntu中, Pool Definitions 區域只有下面這個一樣內容:
include=/etc/php7/fpm/pool.d/*.conf
ContOS則在PHP-FPM主配置檔案的頂部使用下面這行程式碼,引入程序池定義的檔案:
include=/etc/php-fpm.d/*.conf
這行程式碼的作用是讓PHP-FPM載入/etc/php7/fpm/pool.d/目錄(Ubuntu)或/etc/php-fpm.d/目錄(CentOS)中的各個程序池定義檔案. 進入這個目錄, 應該會看到一個名為www.conf的檔案. 這是名為www的預設PHP-FPM程序池的配置檔案.
注意: 每個PHP-FPM程序池的配置檔案開頭都是[符號, 後面跟程序池的名稱的名稱.然後是]符號. 例如, 在預設的PHP-FPM程序池的配置檔案中, 開頭是是[www]
各個PHP-FPM程序池都以指定的作業系統使用者和使用者組的身份執行. 我喜歡以單獨的非根使用者執行各個PHP-FPM程序池, 這樣在命令列中使用top或者ps aux命令時便於識別 每個PHP應用的PHP-FPM程序池. 這是個好喜歡, 因為每個PHP-FPM程序池中的程序都相應的作業系統使用者和使用者組的許可權限制在沙盒中.
我們要配置預設的 www PHP-FPM程序池, 讓他以deploy使用者和使用者組的身份執行.
我建議把以下設定的預設值改為下面列出的值:
-
user = deploy 擁有這個PHP-FPM程序池中子程序的系統使用者, 要把這個設定的值設為執行PHP應用的非根使用者的使用者名稱.
-
group = deploy 擁有這個PHP-FPM程序池中子程序的系統使用者組, 要把這個設定的值設為執行PHP用用的非根使用者所屬的使用者組名.
-
listen = 127.0.0.1:9000 PHP-FPM程序池監聽的IP地址和埠號, 讓PHP-FPM只接受NGINX從這裡傳入的請求. 127.0.0.1:9000 讓指定的PHP-FPM程序吃監聽從本地埠9000進入的連線. 我使用的埠是9000, 不過你可以使用任何不需要特殊許可權(大於1024)且沒有被其他系統程序佔用的埠號. 配置NGINX虛擬主機時,會再次討論這個設定.
-
listen.allowed_clients = 127.0.0.1 可以向這個PHP-FPM程序池傳送請求的IP地址(一個或多個). 為了安全, 我把這個設定為127.0.0.1 即只有當前裝置能把請求轉發給這個PHP-FPM程序池. 預設情況下, 這個設定可能被註釋掉了, 如果需要, 建議去掉這個設定的註釋.
-
pm.max_children = 51 這個設定指定任何時間點, PHP-FPM程序池中最多嫩嫩共有多少個程序. 這個設定沒有絕對正確的值, 你應該測試你的PHP應用, 確定每個PHP程序需要多少記憶體. 然後把這個設定改為裝置可用記憶體能容納的PHP程序總數. 對大小數中小型PHP應用來說,每個PHP程序要使用5-15MB記憶體(具體用量可能有差異). 假設我們使用的裝置為這個PHP-FPM程序池分配了512MB可用記憶體, 那麼我們可以把這個設定為(512總記憶體)/(每個程序使用10MB) = 512個程序.
-
pm.start_servers = 3 PHP-FPM啟動時PHP-FPM程序池中立即可用的程序數. 同樣的,這個設定也沒有絕對正確的值. 對大多數中小型PHP應用來說, 我建議設為2或3. 這麼做是為了先準備好兩到三個程序, 等待請求進入. 不讓PHP應用的頭幾個http請求等待PHP-FPM初始化程序池中的程序.
-
pm.min_spare_servers = 2 PHP應用空閒時PHP-FPM程序池中可以存在的程序數量的最小值. 這個設定的值一般與pm.start_servers設定的值一樣, 用於確保新進入的HTTP去請求無需等待PHP-FPM在程序池中重新初始化程序.
-
pm.max_spare_servers = 4 PHP應用空閒時PHP-FPM程序池中可以存在的程序數量的最大值. 這個設定的值一般比pm.start_servers設定的值大一點. 用於確保新進入的HTTP請求無需等待PHP-FPM在程序池中重新初始化程序.
-
pm.max_requests = 1000 回收程序前, PHP-FPM程序池中各個程序最多能處理的HTTP請求數量. 這個設定有助於避免PHP擴充套件或庫因編寫拙劣而導致不斷洩露記憶體. 我建議為1000, 不過你可以根據應用的雪球做調整.
-
slowlog = /path/to/slowlog.log 這個設定的值是一個日誌檔案中愛檔案系統彙總的絕對路徑. 這個日誌檔案用於記錄處理時間超過N秒的http請求資訊; 以便找出PHP應用功能的平靜, 進行除錯,記住PHP-FPM程序池所屬的使用者和使用者組必須有該檔案的寫許可權. /path/to/slowlog.log只是示例, 請替換成真正的檔案路徑.
-
request_slowlog_timeout = 5s 如果當前的HTTp請求的處理時間超過指定的值,就把請求的回溯資訊寫入slowlog設定的指定的日誌檔案. 把這個設定的值設為多少,取決於你認為多長時間算久. 已開始可以設定為5s
編輯並儲存PHP-FPM的配置檔案後要執行下述命令, 重啟PHP-FPM的主程序:
Ubuntu
sudo service php7-fpm restart
CentOS
sudo systemctl restart php-fpm.service
記憶體
執行PHP時我最關心的時每個PHP程序要使用多少記憶體. php.ini檔案中的memory_limit設定用於設定單個PHP程序可以使用的系統記憶體最大值.
這個設定的預設值是128M, 這對大多數小型PHP應用來說或許合適. 但是如果執行的時微型PHP應用, 可以降低這個值, 例如設為64M, 節省系統資源. 如果 執行的時記憶體集中性PHP應用, 可以增加這個值, 例如設定為512M, 提升效能. 這個設定的值由可用的系統記憶體決定.確定給PHP分配多少值是一門藝術,而不是科學. 決定給PHP分配多少記憶體,以及能負擔得起多少個PHP-FPM程序時,我會問自己一下幾個問題:
-
一共能分配給PHP多少記憶體? 首先我會確定能分配給PHP多少系統記憶體. 例如我可能會使用一個虛擬裝置, 這個裝置一共有2G記憶體.可是這臺裝置還會有其他程序,例如NGINX,MySQL等等. 而這些程序也要消耗記憶體, 我覺得留給512MB給PHP就足夠了.
-
單個PHP程序平均消耗多少記憶體 單後,我會確定單個PHP程序平均消耗多少記憶體. 為此我要監控程序的記憶體使用量.如果使用命令可以使用top命令, 查詢執行中的程序的實時統計資料. 除此之外,還可以在PHP及指令碼的最後呼叫
memory_get_peak_usage()
函式, 輸出當前指令碼消耗的最大記憶體量. 不管使用暗中方式,都要多次運行同一個PHP指令碼,然後取記憶體消耗量的平均值. 我返現PHP程序一般會消耗5-20MB記憶體(記憶體消耗可能會有差異). 如果要上傳檔案,處理圖形,或者執行的時記憶體集中性應用,得到的平均值顯然會高些. -
能負擔得起多少個PHP-FPM程序 假設我給PHP分配了512MB記憶體,每個PHP程序平均消耗15M記憶體 我拿記憶體總量除以每個PHP程序消耗的記憶體量, 從而確定我能負擔得起34個PHP-FPM程序. 這個是估值,應該再做實驗,得到精確值.
-
有足夠的系統資源嗎 最後我會問我自己,確信有足夠的系統資源執行PHP應用並處理預期的流量嗎? 如果答案是肯定的, 大太好了.如果是否定的, 就需要升級服務,在回到第一個問題.
注意
我們應該使用Apache Bench 或者Seige, 在類似的伸長環境的條件下對PHP應用做壓力測試,
因為最好在吧應用部署到生產環境之前確定是否有足夠的資源可用.
PHP開啟OPcache
確定要分配多少記憶體後,我會配置PHP的Zend OPcache擴充套件. 這個擴充套件用於快取操作碼.為什麼要這麼做呢? 我們先來分析每次HTTP請求時,筒倉是如何處理PHP指令碼的. 首先NGINX把HTTP請求轉發給PHP-FPM,PHP-FPM再把請求交給某個PHP子程序處理. PHP程序找到相應的PHP指令碼後,讀取指令碼,把PHP指令碼編譯成操作碼(或位元組碼)格式, 然後執行編譯得到的額操作碼, 生成響應. 最後吧HTTP響應發給NGINX,NGINX再把響應發給HTTP客戶端. 顯而易見,每次HTTP請求都要消耗很多資源.
我們可以快取編譯每個PHP指令碼得到的操作碼,加速這個處理過程. 快取後,我們可以從快取中直接讀取並執行預先編譯好的操作碼. 不用每次處理HTTP請求時都查詢,讀取和編譯PHP指令碼. PHP5.5+內建了Zend OPcache擴充套件.
PHP預設沒有開啟Zend OPcache
編譯的話需要在執行./configure
時新增 --enable-opcache
編譯好之後需要再php.ini
新增zend_extension=opcache.so
啟用Zend OPcache後需要在php.ini中配置Zend OPcache的設定
opcache.memory_consumption = 64
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 4000
//為0時:Zend OPcache不會察覺到PHP指令碼的變化,需要我們手動清空Zend OPcache快取的位元組碼
//但是這樣對開發環境很不利,下面啟動自動重新驗證快取的功能
opcache.validate_timestaps = 1 //在生產環境中設定為0
opcache.revalidate_freq = 0
opcache.fast_shutdown = 1
Zend OPcache部分配置註解
-
opcache.memory_consumption = 64 為操作碼快取分配的記憶體量(單位是MB). 分配的記憶體量應該足夠儲存應用中編譯得到的操作碼. 如果是小型PHP應用,指令碼數較少.可以設定為較低的值. 例如:16MB;如果是大型PHP,應該會有很多指令碼,那叫使用較大的值,例如64MB.
-
opcache.interned_strings_buffer =16 用來儲存駐留字串(interned string)的記憶體量(單位是MB).那麼駐留字串時什麼呢? PHP直譯器在背後會找到相同字串的多個例項,把這個字串儲存在記憶體中. 如果再次使用相同的字串, PHP直譯器會使用指標.這麼做能節省記憶體. 預設情況下,PHP駐留字串會隔離在各個PHP程序中. 這個設定能讓PHP-FPM程序池中的所有程序把駐留字串儲存到共享的緩衝區中. 以便在PHP-FPM程序池中的多個程序之間引用駐留字串. 這樣能節省更多記憶體.這個設定的預設值是4MB, 不過我喜歡設定為16MB.
-
opcache.max_accelerated_files = 4000 操作碼快取中最多能儲存多少個PHP指令碼. 這個設定的值可以使200到100000之間的任何數.我是用的是4000. 這個值一定要比PHP應用中的檔案數量大.
-
opcache.validate_timestamps = 1 這個設定的值為1時,經過一段時間後PHP會檢查PHP指令碼的內容是否有變換. 檢查的時間間隔由opcache.revalidate_freq設定指定.如果這個值設定為0, PHP不會檢查PHP指令碼的內容是否變化,我們必須手動清除快取的操作碼. 我建議在開發環境中設定為1,在生產環境中設定為0.
-
opcache.revalidate_freq = 0 設定PHP多久(單位是秒) 檢查一次PHP指令碼的內容是否有變化. 快取的好處是,不用每次請求都重新編譯PHP指令碼. 這個設定用於確定在多長時間內認為操作碼快取時新的. 在這個時間段之後,PHP會檢查PHP指令碼的內容是否有變化. 如果有變化,PHP會重新編譯指令碼,再次快取.我使用的值是0秒. 僅當opcache.validate_timestamps設定為1時這麼設定會在每次請求時都重新驗證碼PHP檔案. 因此在開發安環境中,每次請求都會重新驗證PHP檔案(這是好事). 這個設定在生產環境中沒有任何意義,因為生產環境中opcache_validate_timstamps的值稽核中為0.
-
opcache.fast_shutdown =1 這麼設定能讓操作碼使用更快的停機步驟,把物件析構和記憶體釋放交給Zend Engine的記憶體管理器完成. 這個設定缺少文件,你只需要知道把他設定為1即可
檔案上傳
如果網站不允許上傳,為了增強應用的安全性,應該禁止檔案上傳功能. 如果你的應用執行上傳檔案,最好設定最大能上傳的檔案大小. 除此之外,最好還要設定最多能同時上傳多少個檔案.
file_uploads = 1
upload_max_filesize =10M
max_file_uploads = 3
預設情況下, PHP允許在單次請求中上傳20個檔案,上傳的每個檔案最大為2MB. 你可能不想云溪同時上傳20個檔案,我只允許單次請求上傳3個檔案.
如果我的PHP應用允許上傳檔案,通產個都會執行上傳大於2MB的檔案.
我把upload_max_filesize
設定的值增加到了10MB.
或許根據應用的需求設定為更高的值,但是最好不要設定的太大.
如果這個值太大,web伺服器會抱怨HTTP請求的主體太大,或者請求會超時.
- 注意
如果需要上傳非常大的檔案,web伺服器的配置需要做對應的調整.
處理在php.ini中設定之外,還需要調整NGINX虛擬主機配置彙總的
client_max_body_size
設定
最長執行時間
php.ini檔案中的max_execution_time
設定用於指定單個PHP程序在終止之前最長可以執行多少時間.
這個設定的預設值是30秒.我們可不想讓PHP程序執行30秒.
因為我們想讓應用執行的特別快.我建議吧這個設定為5秒:max_execution_time =5
- 注意
在PHP指令碼中可以呼叫`set_time_limit()`函式來覆蓋這個函式
你可能會問,如果PHP指令碼需要執行更長的時間怎麼辦. 答案是,PHP指令碼不能長時間執行. PHP指令碼執行時間越長,web應用的訪問者等待響應的時間就會越長. 如果有長時間執行的任務,要在單獨的程序中執行.
建議
我會使用PHP中的exec()函式呼叫bash的at命令,這個命令作用是派生單獨的非阻塞程序.
不耽誤當前的PHP程序.使用PHP中的exec()函式時,要執行escapeshellarg()函式,轉義shell引數.
假設我們要生一個報告,並將結果製作成PDF檔案. 這個檔案可能要花10分鐘才能完成. 而我們肯定不想讓這個PHP請求等待10分鐘. 我們應該單獨編寫一個PHP檔案,例如將其命名為create_report.php讓這個檔案執行10分鐘. 最後生成報告. 其實, web應用只需要幾毫秒就可以派生一個單獨的後臺程序,然後返回http響應. 如下所示
<?php
exec('echo "create_report.php" | at now');
echo 'Report pending...';
create-report.php指令碼在單獨的後臺程序中執行. 執行完畢後可以更新資料庫,或者通過郵件把報告發給收件人. 可以看出,我們完全沒有理由讓長時間執行的任務拖延PHP主指令碼,影響使用者的體驗.
建議 如果發現自己派生了很多後臺程序,或許最好使用專門的作業佇列. 比如PHPResque
處理回話
PHP預設的回話處理程式會拖慢大型應用,因為儲存在磁碟中,需要建立不必要的額檔案IO.
浪費時間.我們應該把資料回話儲存在記憶體中,比如使用Memcached或者Redis.
這麼做還有一個好處是以後便於伸縮,任何一臺分部署PHP-FPM伺服器都能訪問回話資料
若想在PHP中訪問Memcached儲存的書,需要安裝連線Memcached的PECL擴充套件. 然後再把下面兩行新增到php.ini檔案中,把PHP預設的回話儲存訪問改為MEmcached.
session.save_handler = 'memcached'
session_save_path = '127.0.0.1:11211'
緩衝資料
如果在較少的塊中傳送更多的資料,而不是在較多的塊中傳送較少的資料. 那麼網路的效率會更高.也就是說, 在較少的片段中把內容傳遞給訪問者的瀏覽器, 能減少HTTP請求總數.
因此,我們需要讓PHP緩衝輸出.預設情況下,PHP已經棄用和輸出緩衝功能(不過沒在命令列中棄用). PHP緩衝4096位元組的輸出後才會把其中的內容發給web伺服器. 下面是我推薦在php.ini檔案中使用的設定
output_buffering = 4096
imlicit_flush = false
建議 如果想修改輸出緩衝區的大小, 確保使用的值是4(32位系統)或8(64系統)的倍數.
真實路徑快取
PHP會快取應用使用的檔案路勁,這樣每次包含或匯入檔案時就無需不斷的搜尋包含路徑了.
這個快取叫真實路徑快取(realpath cache).
如果執行的時大型PHP檔案,使用了大量檔案,增加PHP真實路徑快取的大小能得到更好的效能.
真實路徑快取的預設大小為16K.
這個快取所需要的準確大小不容易確確定,不過可以使用一個小技巧.
首先,增加真實路徑的大小,設定為特別大的值.例如256k.
然後在一個PHP指令碼的末尾加上print_r(realpath_cache_size())
;
輸出真實路徑快取的真正大小.最後吧這個路徑快取的大小GA為這個真正的值.
我們可以在php.ini檔案中設定真實路徑快取大小:
realpath_cache