在上一篇文章中我們瞭解到,Web Server與PHP之間通過sapi來實現解耦,那這個過程到底是什麼呢。cli、cgi、fastcgi、php-fpm又都是什麼呢。別急,在這篇文章中,我們會對其有一個清晰的瞭解。
首先,cli、cgi、fastcgi都是sapi的一種實現形式,是不是有點模糊。沒問題,下面先對其概念有一個簡單的瞭解。 CLI模式
CLI : Command Line Interface(命令列介面),通過命令列方式,我們無需通過Web Server即可執行PHP指令碼;
CLI模式下,php.ini中的某些配置對其不起作用。如不受php指令碼執行時間的限制(Web Server通常有回應時間的限制);
具體CLI模式下在不同平台下如何執行php指令碼以及傳遞參數,請參考PHP cli模式;
目前,CLI模式下執行php指令碼的情況比較少,究其原因是無法滿足複雜的業務需要,也不能傳遞post參數、上傳檔案,ui互動較差,更適合開發人員使用; CGI
CGI:Common Gateway Interface(公用網關介面),是一種與語言無關的協議。
在進一步學習之前我們先來考慮一下這種情況:Web Server能夠取到瀏覽器傳遞過來的get post參數,而Web應用程式也能接受stdin(標準輸入),如此一來,將Web Server接受到的參數直接傳給Web 應用程式不就OK了嗎。
可事實上有這麼簡單嗎。你想一下,兩個人在做買賣,一個在說法語(Web Server),另一個人在說漢語(Web 應用程式),兩個人之間如何進行交流。任何一方遷就另一方都不太可能。好在兩方對英語還都掌握得OK,那好,那就定一個標準語言:英語,以後兩方就通過英語進行溝通。這裡的英語就類似於CGI,php-cgi就是該協議的一種實現方式。
一個動態請求到達Web Server之後,Web Server會根據cgi協議封裝請求資訊和環境變數組成stdin,通過Tcp連結或socket方式將stdin傳遞給php-cgi。
OK,到目前為止,我們已經知道了請求經過Web Server轉寄之後最終到了php-cgi這裡。那麼,這個php-cgi又做了什麼工作呢。和我們前面章節學習的Zend 引擎、詞法、文法分析又有什麼關係呢。
上面的內容可能部分不夠準確,但大致流程是沒問題的。
只不過,CGI模式也稱為fork-execute-kill 模式:每當有一個請求過來時,Web Server都會啟動一個php-cgi去處理這個請求,請求處理完成之後這個php-cgi就會自動銷毀。對於QPS較小的情況下,CGI模式還好,但對於成百上千的QPS,這個時候的平響就會很長。為什麼這麼說呢。因為Web Server每建立一個php-cgi都是要給其分配記憶體和其他資源的,QPS較大時就會造成記憶體以及其他資源的緊張,最終造成整個平響的超長。
有一點要注意:CGI模式下,php-cgi的啟動是受Web Server控制的。
對於目前很多高並發的網站而言,CGI模式很顯眼不能滿足他們的需求,那有沒有什麼辦法解決一下呢。
回想一下上面php-cgi的產生、銷毀過程:請求達到Web Server → 產生php-cgi進程 → 請求轉寄給php-cgi→php-cgi處理請求 → 返回給Web Server處理結果 → php-cgi 銷毀。有沒有一種方法可以實現php-cgi預產生(可能不夠準確)、常駐記憶體呢。答案是肯定的,這也就是我們下面要說的fastcgi模式。 fastcgi
fastcgi也是一種協議,PHP語言的實現方式為php-cgi。fastcgi是cgi的升級版,既然是升級版,那較cgi又提升了哪些功能呢。
提升點就是php-cgi預產生與常駐記憶體。對於這兩點大家可能不理解,解釋一下。
預產生:在請求到達php-cgi之前就產生一定數量的php-cgi。
常駐記憶體:php-cgi在處理過一個請求之後並不會銷毀,它會一直存在,等待著php-fpm分配的下一個請求。
OK,上面也說了,預產生php-cgi的時候會產生一定的數量。這些php-cgi在Web Server的某個請求轉寄過來之後都能對該請求進行處理,如果每個php-cgi都嘗試進行處理就會造成"驚群效應"。那到底哪個能處理呢。很顯然,我們需要對這些個php-cgi進行一個進程調度,php-fpm出現了。 php-fpm
前面說了,php-fpm是php-cgi的進程管理器。
這裡有一點要注意,CGI協議時,php-cgi的啟動是受Web Server控制的;fastcgi協議時,php-cgi的啟動跟Web Server沒有任何關係了,它只受php-fpm的調度。而且,這個時候Web Server轉寄請求以及傳遞參數給某個php-cgi都需先經過php-fpm的調度,之後再由php-fpm控制這個請求具體交給那個php-cgi處理。
可以將php-fpm獨立運行在非web伺服器上,實現所謂的動靜分離。
盜用別人的一張圖,下面是fasgcgi介面協議下一個用戶端請求響應的完整過程。
備忘:
上面我們說到了CGI存在的詬病,並提出了一種解決方案:預產生、常駐記憶體。除了fastcgi協議之外,其實還有另外一種方式。將php作為Apache的一個擴充編譯進去,在Apache啟動時載入並啟用php_module。這個時候,php-cgi常駐在httpd進程內部。當動態請求到達時,httpd不用再產生php-cgi,而是直接將動態請求轉寄給它內部php-cgi。但是,這也有一個問題:高QPS時效能會下降,因為這種方式產生的php-cgi只有一個。
CGI和fastcgi都只是協議。各種支援和WEB互動的程式設計語言對CGI/fastcgi協議都做了各自的實現(當然,任何一種語言都能寫CGI指令碼),而php上的php-cgi和php-fpm正是php對fastcgi協議的實現。
參考:PHP的命令列模式