1. 程式人生 > >測試PHP-FPM的工作流中的疑惑點

測試PHP-FPM的工作流中的疑惑點

顯示 重試 close 使用 出現 文檔 password innodb 數量

順序比較亂,想到什麽測試什麽,測試環境 PHP7.2 和 MariaDB10.3.11

PHP-FPM是 master/worker 多進程模型
master負責和web-server通訊,把接受到請求分發到一個子進程(worker)處理

worker進程出現異常退出不會影響到master,master會重新啟動一個新的子進程

2. 打開持久化的數據庫連接:
$mysqli = new mysqli(‘p:host‘, ‘user‘, ‘password‘, ‘dbname‘);
註意: 文檔中說 持久化連接不能顯示關閉,$mysqli->close() 無效,實測結果發現:$mysqli->close() 確實無法關閉數據庫資源連接,

但是這句話後面的sql查詢是無法執行的,但是不影響下一個php請求。


3. 打開非持久化的數據庫連接
$mysqli = new mysqli(‘host‘, ‘user‘, ‘password‘, ‘dbname‘);

如果使用持久化方式連接數據庫,當子進程完成任務後不會立馬關閉與數據庫的連接,當下一個請求任務過來後會繼續使用這個連接,
(這裏持久化只是PHP不主動關閉連接,但不保證MySQL不主動斷開,MySQL可以通過配置wait_timeout來主動關閉長時間沒有活動的連接)
非持久連接方式會在任務處理完後立即關閉數據庫連接

持久連接帶來了一個直觀的好處,性能提升,但也帶來了很多不可預知的錯誤,比如上一個連接開啟了事務但是沒有提交,那後面連接上的操作

會出現問題

持久化連接的數據庫句柄是保存在子進程的,如果有30個子進程都連接了數據庫,那麽這30個子進程都會保持和數據庫的連接,
所以有個地方需要特別註意,MySQL的最大連接數一定要設置為大於PHP子進程最大數量,否則MySQL會出現 too many connections 錯誤

查看MySQL當前連接數(每隔一秒刷新一次)
mysqladmin -uroot -p123456 -i 1 processlist

查看InnoDB的事務和鎖
SHOW ENGINE INNODB STATUS;

4. 如果PHP子進程正常/意外退出,MySQL連接會不會自動釋放?

不管子進程是正常退出(處理完請求)還是意外退出(直接kill掉),
也不管和數據庫是長連接還是短鏈接,數據庫都會自動斷開

5. 如果瀏覽器發送一個復雜的請求,導致服務器返回超時,會發生什麽?
這個要分兩種情況:
Nginx超時:服務器返回504,這個時候PHP子進程不受影響繼續執行,和數據庫的連接也不會斷開,最終任務會成功執行(前提是PHP不超時)
這種情況很糟糕,用戶看到的是失敗,後臺卻執行成功了,用戶可能會反復重試多次,造成數據錯亂。
PHP超時:服務器返回502,這個時候PHP子進程會被Master殺掉並重啟一個子進程,所以任務是沒有執行成功的,數據庫資源也會自動斷開

6. 如果子進程連接數據庫並開啟了事務,但是最後沒有提交,會發生什麽?
分兩種情況:
數據庫為非持久連接: 這種情況下數據庫連接都關閉了,就別操心事務了
數據庫持久連接:這個情況下官方的說法是要銀熊後面的請求,但是實際測試發現,沒有影響,如果實在擔心的話,可以使用函數 register_shutdown_function

對每個請求結束前處理一下

register_shutdown_function(function() {
  echo ("shutdown process");
});

7. 做一個實驗:配置PHP-FPM只啟動一個子進程,瀏覽器連續發送3個請求,PHP處理程序中sleep 25秒
觀察現象:
通過top可以看到確實始終只有一個 PHP子進程
通過 mysqladmin 查看,始終只有一個連接
第一個請求在25秒後返回了數據
第二個請求在第50秒返回了數據
第三個請求返回504(Nginx默認60s超時)
結論: 一個PHP子進程只能同時處理一個請求,多余的未分配的請求任務堆積在Master主進程中等待空閑子進程

8. 既然PHP子進程某一瞬間只能同時處理一個任務,
從這個理論出發,有人就會有疑問了,是不是服務器開啟100個PHP子進程,那麽這臺服務器的最大並發就是100?
這個地方需要弄清楚並發的概念,並發並不是只一瞬間能處理多少請求,而是指在正常人可以接收的等待時間內(比如2秒)
可以處理多少個請求,所以並發數量是遠遠超過100的

測試PHP-FPM的工作流中的疑惑點