您好,登錄后才能下訂單哦!
這篇文章主要講解了“PHP-FPM多進程模型相關知識點”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“PHP-FPM多進程模型相關知識點”吧!
首先,讓我們一起看幾個問題:
①:PHP-FPM 啟動進程的方式主要有哪幾種,區別是什么?
②:PHP-FPM,是主進程接收請求轉給子進程,還是子進程單獨接收請求并處理,如何驗證?
③:為何在 PHP-FPM 模式下,PHP 代碼很少有人去做連接池?
④:PHP-FPM 模式性能差的體現有哪些,如何優化?
⑤:PHP-FPM 模式下的 YAC 為何無法和 CLI 模式無法共享內存?
1. 如何啟動進程
PHP-FPM 是多進程模式,由 Master 進程管理 Worker 進程。進程的數量,都可以通過 php-fpm.conf 做具體配置。 PHP-FPM 的進程可以分為動態模式及靜態模式:
①:靜態(Static)
直接開啟指定數量的 PHP-FPM 進程,不再增加或者減少;啟動固定數量的進程,占用內存高。但在用戶請求波動大的時候,對 Linux 操作系統進程的處理上耗費的系統資源低。
②:動態(Dynamic)
開始時開啟一定數量的 PHP-FPM 進程,當請求量變大的時候,動態增加 PHP-FPM 進程數到上限,當空閑的時候自動釋放空閑進程數到一個下限。
動態模式會根據 max、min、idle children 配置,動態的調整進程數量。在用戶請求較為波動,或者瞬間請求增高的時候,動態模式下會進行大量進程的創建、銷毀等操作,而造成 Linux 負載波動升高。簡單來說,請求量少,PHP-FPM 進程數少,請求量大,進程數多。優勢就是,當請求量小的時候,進程數少,內存占用也小。
③:按需 (Ondemand)
這種模式下,PHP-FPM 的 Master 不會 Fork 任何子進程,純粹就是按需啟動。
這種模式通常很少使用,因為它基本無法適應有一定量級的線上業務。由于 php-fpm 是短連接的,所以每次請求都會先建立連接,建立連接的過程必然會觸發上圖的執行步驟。所以,在大流量的系統上 Master 進程會變得繁忙,占用系統 CPU 資源,不適合大流量環境的部署。
借用一張網絡圖片來說明:
需要注意 2 個點,「連接」和「數據」到來。有連接進來再 Fork 進程,同樣可以達到子進程繼承父進程上下文,然后子進程處理用戶請求這個目的。
(關于動態、靜態進程模式的相關參數,可參考 PHP 官方文檔。)
我們需要關注的是對于我們自身的業務,應該選擇的 PHP-FPM 模式為動態還是靜態。
通常來說,對于比較大內存的服務器,設置為靜態的話會提高效率。因為頻繁開關 php-fpm 進程也會有時滯,所以內存夠大的情況下開靜態效果會更好。數量也可以根據 內存/30M 得到。比如說 2GB 內存的服務器,可以設置為 50;4GB 內存可以設置為 100 等。高配機器選靜態,低配機器(省內存)選動態,高配機器用動態不能充分利用內存資源和 CPU 資源,也無法及時應對瞬時高并發。
2. 如何進行請求處理和驗證
PHP-FPM 的進程管理方式和 Nginx 的進程管理方式有些類似。在處理請求時,并非由主進程接受請求后轉給子進程,而是子進程「搶占式」地接受用戶請求。本質上 PHP-FPM 多進程以及 Nginx 多進程,都是在主進程監聽同一個端口后,Fork 子進程達到多個進程監聽同一端口的目的。
Linux 系統所有的進程 IO 操作,都需要和操作系統打交道。也就是說,系統知道所有 IO 操作。這個過程就是我們常說的「系統調用」。我們可以從系統調用入手解決這個問題。系統調用的查看,可以使用 Strace。
如何驗證相對簡單,我們可以采取 2 種方式:
看 PHP-FPM 進程的日志。這需要配置好合適的 PHP-FPM 日志格式;
既然 IO 數據會通過內核態過度到用戶態進程,我們可以通過 strace -p <pid> 命令去跟蹤系統調用。分別跟蹤 PHP-FPM 的主進程 ID 以及子進程 ID,然后訪問 Nginx,由 Nginx 通過 fast-cgi 協議轉到 PHP-FPM 進程上,看在哪個進程上發送了系統調用。
3. 為何不在 PHP-FPM 下做代碼連接池 ?
首先,在 PHP-FPM 模式下,一個請求的生命周期注定只有 1 次。也就是說,從 FPM 請求到請求、解析 PHP 腳本,到 FPM 的 Zend 虛擬機分配資源執行,再到最后的處理結束,PHP-FPM 會回收這次請求的所有資源。
這種方式一是為了讓開發不需要關心資源的回收處理,所以你可能沒怎么關心過網絡的關閉、文件描述符的關閉等等。二是為了減少內存溢出的情況。
如果在這種模式下,你實現了連接池,也意味著請求結束,連接池消失,做了一次無用功而已。
「雞肋的」PConnect(持久化鏈接)。持久化鏈接也就是鏈接不釋放。但問題在于,PHP-FPM 是多進程模式,而持久化的鏈接存在于進程中。這就意味著,如果一臺機器有 300 個 FPM 進程,會一次性初始化 300 個持久化鏈接。如果因為面臨業務活動需求冒然對機器擴容,很可能造成業務的數據庫連接數直接打滿。
4. 如何優化性能
首先,我們應該思考導致性能差可能的原因是什么。如果一個應用的性能差,我們往往會從 2 個方面來分析,一個是 IO 性能,一個是計算性能。
IO 方面,因為 PHP-FPM 模式下難以做連接池,所以高并發業務下的網絡處理會有劣勢。注意我這里一直說的都是 PHP-FPM 模式下,在 CLI 模式下還是可以自己做連接池的。只不過這個連接池僅限于 CLI 模式的單進程內,而且這個模式不能用于處理網絡請求(比如 HTTP 請求)。因為 PHP 默認單進程模式,FPM、CLI 都是默認單進程,即便 CLI 可以做連接池 ,也不方便做鏈接保活(不能同時做心跳檢測)。
計算性能上來說,雖然 PHP 是用 C 寫的,如果單純論計算性能是不錯的。但問題在于 PHP 處理請求時,每次都要解析 PHP 腳本、翻譯 PHP 代碼為 Opcode、用 Zend 虛擬機執行 Opcode,處理結束,釋放資源。經歷這樣的過程 是導致 PHP 計算性能慢的最大原因之一。
如何優化:
對于計算性能來說,使用 Zend OPcache 擴展,緩存字節碼。
對于** IO 性能**來說,使用文件 cache 或者 memcached 減輕對網絡 Cache 的壓力;使用 Yac 減輕對 Cache 層的壓力;在同一次請求中;復用鏈接不要每次都用新的;合理設計日志組件類庫,優化 Logger 減少對文件操作的次數來減少 IO 的壓力。
關于設計一個合格的 Logger 組件,我們需要注意幾個點:
① 每次請求,只做一次日志寫操作,不要每次別人調用你的函數,你都去執行一次類似 file_put_contents 的操作。
② 兼容各種類似錯誤。換句話說,即使 PHP fatal error 了,你也得能把知名錯誤之前的日志記錄下來。這個實現可以借助 PHP 類的析構方法來做。也可以使用更好的 register_shutdown_function 來注冊一個鉤子,在 PHP 請求結束的時候,回調此鉤子,完成做最后的日志操作。
5. YAC 為何無法和 CLI 模式共享內存
我們知道,PHP 擴展開發中首要執行的一個宏是 PHP_MINIT_FUNCTION。YAC 擴展需要在 PHP-FPM 進程啟動時起就初始化一塊共享內存,供各個進程來共享使用。因此,實現共享的關鍵在于需要一個讓各個進程都知道的相同標識。
YAC 擴展的初始化流程為:
我們查看 create_segments 的具體實現:
上面做了一些注釋,最關鍵的是要開啟共享內存需要的系統 ID,shared_segment_name,此值包含了進程 ID。也就是 PHP-FPM 的主進程 ID。有相同的共享內存標識 ID,就是 PHP-FPM 模式所有進程間能夠通信的奧秘所在。而如果我們是想要通過 PHP 腳本使用 yac 擴展讀取這個共享內存,會這樣做:
在 CLI 模式下,這樣是不可能拿到 PHP-FPM 模式下設置的共享內存數據的。因為 CLI 模式下執行 PHP 腳本、進程 ID,和 PHP-FPM 模式下的進程 ID 完全不相同。
感謝各位的閱讀,以上就是“PHP-FPM多進程模型相關知識點”的內容了,經過本文的學習后,相信大家對PHP-FPM多進程模型相關知識點這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。