1 回答

TA貢獻2012條經驗 獲得超12個贊
OP 有許多棘手的問題擺在桌面上。然而,我覺得這些都是值得關注的(我自己也曾與它們斗爭過),所以讓我們把它拆開。為了大正義;主屏開啟:
解決并發請求問題
(L)AMP 堆棧中的并發連接存在多種可能的問題和解決方案。然而,在討論調整 Apache 和 MySQL 之前,讓我先介紹一個常見的“神秘”問題,該問題會導致并發問題;即,一個必要的罪惡稱為“?PHP 會話鎖定”。
PHP 會話阻塞和并發請求
簡而言之:當您在應用程序中使用會話時,調用 后session_start()
,PHP 會鎖定存儲在您目錄中的會話文件session.save_path
。該文件鎖將保持不變,直到腳本結束或被session_write_close()
調用。結果:同一用戶的任何后續調用都將排隊,而不是同時處理,以確保不會損壞會話數據。(想象一下并行腳本寫入相同的內容$_SESSION
!)
演示這一點的一個簡單方法是創建一個長時間運行的腳本;然后在瀏覽器中調用它;然后打開一個新選項卡,并再次調用它(或者實際上,調用共享相同會話 cookie/ID 的任何腳本)。您將看到,在第一個調用結束之前,第二個調用不會執行。這是奇怪的 AJAX 延遲的常見原因,尤其是來自單個頁面的并行 AJAX 請求。處理將是連續的而不是并發的。然后,10 個調用,每次調用 0.3 秒,總共需要 3 秒才能結束,依此類推。我們不希望那樣,是嗎!
您可以通過確保以下內容來補救由 PHP 會話鎖定引起的請求阻塞:
使用會話的腳本應在會話數據存儲完成后調用
session_write_close()
。會話鎖將立即釋放。不需要會話的腳本一開始就不應該啟動會話。
只需要讀取會話數據的腳本:使用
session_start()
with['read_and_close' => true]
選項將為您提供一個只讀(非持久)$_SESSION
變量,而無需會話鎖定。(自 PHP 7 起可用。)
選項 1 和 3 將為您提供對$_SESSION
變量的讀取訪問權限并釋放/避免會話鎖定。$_SESSION
會話關閉后所做的任何更改都將被默默丟棄;不顯示警告/錯誤。
會話鎖定請求阻塞問題僅對單個用戶(使用同一會話)產生影響。
Apache 和 MySQL 并發請求
曾幾何時,在意識到 PHP 是阻塞/排隊我的并發調用的罪魁禍首之前,我花了一小段時間來調整 Apache 和 MySQL,并想知道會發生什么?
Apache 2.4默認支持150個并發請求;任何進一步的請求都將排隊。MPM/多處理模塊下有多個設置,您可以調整它們以支持所需的并發連接級別。
MySQLmax_connections
有(默認 151)和max_user_connections
(默認無限制)選項。如果您的應用程序為每個用戶發送大量并發請求,您將需要確保全局最大連接足夠高,以確保少數用戶不會占用整個 DBMS。
取消對 Apache/PHP/MySQL 的請求
就您的應用程序的具體接線而言,我們沒有太多可做的,但我從評論中了解到,就目前情況而言,用戶可以在前端取消請求,但不會采取任何后端操作。(即任何后端響應都會被忽略/丟棄。)
“有沒有辦法讓 Apache 刪除已取消的請求?”?我假設您的前端直接無延遲地將請求發送到 Apache;然后轉到 PHP > MySQL > PHP > Apache。在這種情況下,不,你不能真正讓 Apache 取消它已經收到的請求;或者你可以點擊“停止”,但很可能 PHP 和 MySQL 已經把它吃掉了......
持有“取消窗口”
但是,您可以將“取消窗口”延遲編程到前端,其中請求僅在等待可能的取消的 0.5 秒睡眠后才傳遞到 Apache。這可能會對用戶體驗產生負面影響,也可能不會產生負面影響;如果取消了很大一部分請求,則可能值得實施以節省服務器資源。這假設 UI 帶有 Javascript。如果您直接對 API 進行 HTTP 調用,則可以使用“休眠代理接收器”。
使用“取消控制器”
如何取消 PHP/MySQL 進程?顯然,只有當對 API 的調用導致處理時間較長時,這才是可行的/可行的。如果后端需要 0.28 秒來處理,并且用戶在 0.3 秒后取消,那么就沒有太多可以取消的了,是嗎?
但是,如果您確實有可能運行更長時間的腳本,例如幾秒鐘。您總是可以在代碼中找到相關的斷點,其中有“未取消”檢查或終止/回滾例程?;旧希鷮⒕哂幸韵铝鞒蹋?/p>
前端將帶有唯一ID的請求發送到主腳本
PHP 腳本開始構建響應的長征
取消時:前端將 ID 重新發送到輕量級取消控制器
取消控制器將 ID 記錄到臨時文件/數據庫/任何地方
PHP 在斷點處檢查當前進程是否有取消請求
取消時,PHP 執行終止/回滾例程而不是進一步處理
這種“取消監視”顯然會產生一些開銷,因此您可能只想將其合并到較重的腳本中,以確保您實際上在整體上節省了一些處理時間。此外,您最多只需要在重要的交匯處設置幾個斷點。對于讀取請求,您可以直接終止該進程;但對于寫入請求,您可能希望進行正?;貪L以確保系統中的數據完整性。
您還可以使用mysqli::?kill 取消/終止已由 PHP 啟動的長時間運行的 MySQL 線程。為了使這一點有意義,您需要將其運行為MYSQLI_ASYNC
,這樣 PHP 就可以停止使用了。PDO 似乎沒有針對異步查詢或終止的本機等效項。
PHP 連接處理
作為“從側面”傳遞取消信號的控制器的替代方案,您可以查看PHP 連接處理并使用connection_aborted()
.?(有關代碼示例,請參閱上面的“MySQL Kill”鏈接。)
CONNECTION_ABORTED
如果用戶單擊瀏覽器中的“停止”按鈕,則會出現一種狀態。PHP 有一個ignore_user_abort()
設置,默認為“Off”,它應該在用戶中止時中止腳本。(不過,根據我的經驗,如果我有一個流氓腳本并且會話鎖定處于打開狀態,那么即使我在瀏覽器中點擊“停止”,我也無法執行任何操作,直到超時。想想看。)
如果您將“忽略用戶中止”設置為 false,即。PHP 腳本在用戶中止時終止,請注意,這將是完全不受控制的終止,除非您已register_shutdown_function()
實現。即便如此,您也必須在代碼中標記檢查點,以便關閉函數能夠從終止點向前“倒回時鐘”。另請注意此警告:
我沒有通過 AJAX/JS 實現“用戶中止”的經驗。
- 1 回答
- 0 關注
- 111 瀏覽
添加回答
舉報