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