最近小峰在開發項目的時候遇到一個問題就是阻塞問題。用的是thinkphp架構,利用ajax請求一次操作,在ajax沒有返回結果前,相同程式,其它操作是無效的。然後網上一查,有人和我有一樣的問題,下面把該解決方案分享出來。
當同時向服務端發現若干HTTP請求,有時你會發現這些請求可能並非並發完成的,伺服器對這些請求進行了排隊處理,產生了所謂的PHP阻塞現象。最有可能是指令碼進行了session資料的讀寫,PHP中session預設使用檔案系統進行儲存的,當進行讀寫session檔案操作時,儲存session的檔案處於鎖定狀態,此時其他需要讀寫session資料的請求需要等待前一個請求完成後才會進行,從而導致PHP阻塞的發生,慶幸的是PHP提供了session_write_close()函數來結束當前session並寫入資料。
session阻塞簡單示範
建立2個php檔案:session_a.php,session_b.php。
<?php
// session_a.php
session_start();
$_SESSION['a'] = date('H:i:s');
// session_write_close();
sleep(5);
echo $_SESSION['a'];
<?php
// session_b.php
session_start();
$_SESSION['b'] = date('H:i:s');
// session_write_close();
sleep(5);
echo $_SESSION['b'];
同時訪問這2個指令碼,你會發現,其中一個指令碼比另一個延遲了5秒。而當我們將檔案中的session_write_close()函數注釋取消掉後,再來同時訪問發現2個指令碼可以同時執行了。
session鎖定處理機制
順便提一下,session_commit()是session_write_close()的別名,即也可以使用前者替代後者。
當session_start()調用時,session處理機制預設會開啟或建立一個seesion檔案,且會立即給這個檔案上了一個鎖定狀態(locked)。當session_commit()調用時或指令碼執行完成後該檔案會被解鎖(unlocked)。
鎖定狀態有個重要的影響:同時請求使用了session的PHP指令碼,並非並列執行的,而是分離的。如果當使用者發起了一個請求,同時發起另一個請求便會被阻塞,直至前一個請求完全完成。
Session鎖定的好處
請不要勿以為這所謂的阻塞現象是PHP的BUG,當然不是,相反有些時候分離執行才是正確的做法。考慮一下購物車案例:
使用者發起A請求,指令碼讀取用來顯示購物車物品的session資料;
在A請求完成之前,使用者便點擊了“加入購物車”按鈕,發送了個B請求;
B等待A請求完成,然後向session中新增資料;
如果沒有對session進行鎖定會發生什嗎?
B沒有等待A完成,讀取並寫入session資料;
A請求完成並寫入之前讀取的session資料,覆蓋了上述B寫入的資料;
所以,我們在使用session時應當考慮當前實際環境。
ThinkPHP如何解決session阻塞
最近開始使用國內的PHP架構ThinkPHP,便遇見了阻塞問題,因為沒仔細看官方文檔,調試許久未果,差點一怒之下放棄該架構,後發現配置項裡有個“SESSION_AUTO_START”配置,用於自動載入session,果斷設定為FALSE,一切恢複正常。
在需要使用session的時候,可以使用PHP內建函數:
session_start();
//...
session_commit(); // 或session_write_close()
也可以用TP風格方式:
session('[start]');
//...
session('[pause]');