1. 程式人生 > >零基礎學習PHP程式設計——詳解Apache的MPM及常用的PHP模式

零基礎學習PHP程式設計——詳解Apache的MPM及常用的PHP模式

詳解Apache的MPM及常用的PHP模式

注意: 本文主要來源於網上的文章(詳見參考資料),介紹了Apache多處理模組,以及Apache常用的PHP模式。

歡迎交流QQ群: 640765823 

一、Apache的多處理模組MPM

Apache HTTP 伺服器被設計為一個功能強大,並且靈活的 web 伺服器, 可以在很多平臺與環境中工作。不同平臺和不同的環境往往需要不同 的特性,或可能以不同的方式實現相同的特性最有效率。Apache 通過模組化的設計來適應各種環境。這種設計允許網站管理員通過在 編譯時或執行時,選擇哪些模組將會載入在伺服器中,來選擇伺服器特性。

Apache 2.0 擴充套件此模組化設計到最基本的 web 伺服器功能。 它提供了可以選擇的多處理模組(MPM)

,用來繫結到網路埠上,接受請求, 以及排程子程序處理請求。

擴充套件到這一級別的伺服器模組化設計,帶來兩個重要的好處:

Apache 能更優雅,更高效率的支援不同的平臺。尤其是 Apache 的 Windows 版本現在更有效率了,因為 mpm_winnt 能使用原生網路特性取代在 Apache 1.3 中使用的 POSIX 層。它也可以擴充套件到其它平臺 來使用專用的 MPM。

Apache 能更好的為有特殊要求的站點定製。例如,要求 更高伸縮性的站點可以選擇使用執行緒的 MPM,即 worker 或 event; 需要可靠性或者與舊軟體相容的站點可以使用 prefork。

在使用者看來,MPM 很像其它 Apache 模組。主要是區別是,在任何時間, 必須有且只有一個 MPM模組被載入到伺服器中

。可用的 MPM 列表位於 模組索引頁面(http://httpd.apache.org/docs/2.2/mod/)。

1.1 選擇 MPM

MPM 必須在編譯之前,配置時指定,然後編譯到伺服器程式中。 僅當編譯器知道使用了執行緒時,它才有能力優化許多函式。

為了使用指定的 MPM,請在執行 configure 時,使用引數 –with-mpm=NAME。NAME 是指定的 MPM 名稱。

編譯完成後,可以使用 ./httpd -l 來確定選擇的 MPM。 此命令會列出編譯到伺服器程式中的所有模組,包括 MPM。

預設 MPM

下表列出了不同系統的預設 MPM。如果你不在編譯時選擇,那麼它就是你將要使用的 MPM。

BeOS    beos


Netware    mpm_netware
OS/2    mpmt_os2
Unix    prefork
Windows    mpm_winnt

常見的MPM模組有以下幾種:

beos
This Multi-Processing Module is optimized for BeOS.

event
An experimental variant of the standard worker MPM

mpm_netware
Multi-Processing Module implementing an exclusively threaded web server optimized for Novell NetWare

mpmt_os2
Hybrid multi-process, multi-threaded MPM for OS/2

prefork
Implements a non-threaded, pre-forking web server

mpm_winnt
This Multi-Processing Module is optimized for Windows NT.

worker
Multi-Processing Module implementing a hybrid multi-threaded multi-process web server

1.2 Linux上Apache支援的三種MPM模式

Apache2.4.25總共支援三種MPM(多程序處理模組)模式,分別是Prefork、worker及event,這三種模式代表了Apache的演變和發展。

Apache2.2中,預設啟用prefork模式,同時引進了實驗性質的event模式;

Apache2.4中,正式支援並且預設使用了event模式。並且可以在編譯的時候增加了選項enable-mpms-shared來編譯MPM並在編譯後可以動態載入。

可以通過apachectl -V檢視當前Apache的工作模式,比如在Apache2.2.31上檢視:

Server version: Apache/2.2.31 (Unix)
Server built:   Dec 25 2016 07:02:07
Server’s Module Magic Number: 20051115:40
Server loaded:  APR 1.5.2, APR-Util 1.5.4
Compiled using: APR 1.5.2, APR-Util 1.5.4
Architecture:   64-bit
Server MPM:     Prefork
threaded:     no
forked:     yes (variable process count)

Apache2.4.25上檢視:

$ /usr/local/apache24/bin/httpd -V
Server version: Apache/2.4.18 (Unix)
Server built:   Feb 18 2016 02:28:26
Server’s Module Magic Number: 20120211:52
Server loaded:  APR 1.5.2, APR-UTIL 1.5.4
Compiled using: APR 1.5.2, APR-UTIL 1.5.4
Architecture:   64-bit
Server MPM:     event
threaded:     yes (fixed thread count)
forked:     yes (variable process count)

1.3 Prefork MPM詳解

Prefork MPM實現了一個非執行緒、預派生的工作模式。它在Apache啟動之初,就會預派生一些子程序,然後等待連線。可以減少頻繁建立和銷燬程序的開銷,每個子程序只有一個執行緒。它成熟穩定,可以相容新老模組,也不需要擔心執行緒安全問題。但是一個程序相對地佔用更多的資源,消耗大量記憶體,不擅長處理高併發的場景。

    <IfModule mpm_prefork_module>
StartServers          5
MinSpareServers       5
MaxSpareServers      10
MaxClients          150
MaxRequestsPerChild   0
</IfModule>

MaxClients設定Apache可同時處理的請求數量,其對Apache效能的影響非常打。預設的150遠遠不能滿足一般站點(ps -ef | grep httpd | wc -l),超過這個數量的請求需要排隊,直到前面的請求處理完畢。如果我們發現系統資源還剩很多,但是HTTP訪問卻很緩慢,大多數時候增加這個值可以得到緩解。
這個值的計算公式可以參考:

apache_max_process_with_good_perfermance < (total_hardware_memory / apache_memory_per_process ) * 2 ;

apache_max_process = apache_max_process_with_good_perfermance * 1.5 ;

其中httpd平均佔用記憶體

ps aux|grep -v grep|awk ‘/httpd/{sum+=$6;n++};END{print sum/n}’

MaxRequestsPerChild這個值的含義是處理多少個請求後該程序自動銷燬,預設值0意味著永不銷燬。當負載較高時,為了使每個程序處理更多的請求,避免銷燬、建立程序的開銷,一般建議設定為0或較大的數字。但是也要注意可能會造成程序佔用的記憶體不能得到釋放,所以這個值不能設定得太大,也不能太小,大了會影響資源的釋放,小了會導致Apache不斷地fork程序。

建議值:1分鐘pv(訪問量)/MaxClients

1.4 worker MPM詳解

Prefork工作模式相比,worker使用了多程序和多執行緒的混合模式,worker模式也同樣會預派生一些子程序,然後每個子程序建立一些執行緒,同時包括一個監聽執行緒,每個請求過來會被分配到一個執行緒來服務。執行緒比程序更加輕量級,因為執行緒通常會共享父程序的記憶體地址的,因此記憶體佔用會減少一些。

同時如果一個執行緒異常掛了,會導致父程序和它的其他正常子執行緒都掛了,這樣也只會影響Apache的一部分,而不是整個服務。

缺點使必須考慮執行緒安全性,因為多個子程序是共享父程序的記憶體地址的。如果使用keep-alive的長連線方式,某個執行緒會被一直佔據,也許中間沒有任何請求,需要等到超時才會被釋放。如果過多的執行緒被這樣佔據,也會導致在高併發下的無服務執行緒可用。

1.5 event MPM詳解

和worker工作模式很像,最大的區別是event模式解決了在keep-alive場景下,長期被佔用的執行緒的資源浪費問題,在event MPM中,會有一個專門的執行緒來管理這些keep-alive執行緒,當有真實請求過來的時候,將請求傳遞給服務執行緒,執行完畢後,又允許它釋放,這樣增強了在高併發場景下的請求處理能力

二、Apache採用的PHP模式

在搭建 LAMP/LNMP 伺服器時,會經常遇到 PHP-FPM、FastCGI和CGI 這幾個概念。如果對它們一知半解,很難搭建出高效能的伺服器,接下來我們就以圖形方式,解釋這些概念之間的關係。

在整個網站架構中,Web Server(如Apache)只是內容的分發者。舉個栗子,如果客戶端請求的是 index.html,那麼Web Server會去檔案系統中找到這個檔案傳送給瀏覽器,這裡分發的是靜態資料。

如果請求的是 index.php,根據配置檔案,Web Server知道這個不是靜態檔案,需要去找 PHP 解析器來處理,那麼他會把這個請求簡單處理,然後交給PHP解析器
當Web Server收到 index.php 這個請求後,會啟動對應的 CGI 程式,這裡就是PHP的解析器。接下來PHP解析器會解析php.ini檔案,初始化執行環境,然後處理請求,再以規定CGI規定的格式返回處理後的結果,退出程序,Web server再把結果返回給瀏覽器。這就是一個完整的動態PHP Web訪問流程,接下來再引出這些概念,就好理解多了,

CGI是 Web Server 與 Web Application 之間資料交換的一種協議

FastCGI同 CGI,是一種通訊協議,但比 CGI 在效率上做了一些優化。同樣,SCGI 協議與 FastCGI 類似。

PHP-CGI:是 PHP (Web Application)對 Web Server 提供的 CGI 協議的介面程式

PHP-FPM:是 PHP(Web Application)對 Web Server 提供的 FastCGI 協議的介面程式,額外還提供了相對智慧一些任務管理。

在WEB 中,

Web Server 一般指Apache、Nginx、IIS、Lighttpd、Tomcat等伺服器
Web Application 一般指PHP、Java、Asp.net等應用程式

2.1 Module方式

在瞭解 CGI 之前,我們先了解一下Web server 傳遞資料的另外一種方法:PHP Module載入方式。以 Apache 為例,在PHP Module方式中,是不是在 Apache 的配置檔案 httpd.conf 中加上這樣幾句:

加入以下2句
LoadModule php5_module D:/php/php5apache2_2.dll
AddType application/x-httpd-php .php

修改如下內容
<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>

上面是 Windows 下安裝php和apache環境後手動配置,在linux下原始碼安裝大致是這樣配置的:

./configure –with-mysql=/usr/local –with-apache=/usr/local/apache –enable-track-vars

所以,這種方式,他們的共同本質都是用 LoadModule 來載入 php5_module,就是把php作為apache的一個子模組來執行。當通過web訪問php檔案時,apache就會呼叫php5_module來解析php程式碼

那麼php5_module是怎麼來將資料傳給php解析器來解析php程式碼的呢?答案是通過sapi。

我們再來看一張圖,詳細的說說apache 與 php 與 sapi的關係


從上面圖中,我們看出了sapi就是這樣的一箇中間過程SAPI提供了一個和外部通訊的介面,有點類似於socket,使得PHP可以和其他應用進行互動資料(apache,nginx等)。php預設提供了很多種SAPI,常見的提供給apache和nginx的php5_module、CGI、FastCGI,給IIS的ISAPI,以及Shell的CLI。

所以,以上的apache呼叫php執行的過程如下:

apache -> httpd -> php5_module -> sapi -> php

好了。apache與php通過php5_module的方式就搞清楚了吧!

這種模式將php模組安裝到apache中,所以每一次apache結束請求,都會產生一條程序,這個程序就完整的包括php的各種運算計算等操作。

在上圖中,我們很清晰的可以看到,apache每接收一個請求,都會產生一個程序來連線php通過sapi來完成請求,可想而知,如果一旦使用者過多,併發數過多,伺服器就會承受不住了。

而且,把mod_php編進apache時,出問題時很難定位是php的問題還是apache的問題。

2.2 CGI

CGI(Common Gateway Interface)全稱是“通用閘道器介面”,WEB 伺服器與PHP應用進行“交談”的一種工具,其程式須執行在網路伺服器上。CGI可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環境變數。如php、perl、tcl等。

WEB伺服器會傳哪些資料給PHP解析器呢?URL、查詢字串、POST資料、HTTP header都會有。所以,CGI就是規定要傳哪些資料,以什麼樣的格式傳遞給後方處理這個請求的協議。仔細想想,你在PHP程式碼中使用的使用者從哪裡來的。

也就是說,CGI就是專門用來和 web 伺服器打交道的。web伺服器收到使用者請求,就會把請求提交給cgi程式(如php-cgi),cgi程式根據請求提交的引數作應處理(解析php),然後輸出標準的html語句,返回給web服伺服器,WEB伺服器再返回給客戶端,這就是普通cgi的工作原理。

CGI的好處就是完全獨立於任何伺服器,僅僅是做為中間分子。提供介面給apache和php。他們通過cgi搭線來完成資料傳遞。這樣做的好處了儘量減少2個的關聯,使他們2變得更獨立。

但是CGI有個蛋疼的地方,就是每一次web請求都會有啟動和退出過程,也就是最為人詬病的fork-and-execute模式,這樣一在大規模併發下,就死翹翹了。

2.3 FastCGI介紹

FastCGI簡單介紹
從根本上來說,FastCGI是用來提高CGI程式效能的。類似於CGI,FastCGI也可以說是一種協議。

FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行著,只要啟用後,不會每次都要花費時間去fork一次。它還支援分散式的運算, 即 FastCGI 程式可以在網站伺服器以外的主機上執行,並且接受來自其它網站伺服器來的請求。

FastCGI是語言無關的、可伸縮架構的CGI開放擴充套件,其主要行為是將CGI直譯器程序保持在記憶體中,並因此獲得較高的效能。眾所周知,CGI直譯器的反覆載入是CGI效能低下的主要原因,如果CGI直譯器保持在記憶體中,並接受FastCGI程序管理器排程,則可以提供良好的效能、伸縮性、Fail- Over特性等等。

FastCGI的工作原理
FastCGI介面方式採用C/S結構,可以將HTTP伺服器和指令碼解析伺服器分開,同時在指令碼解析伺服器上啟動一個或者多個指令碼解析守護程序。當HTTP伺服器每次遇到動態程式時,可以將其直接交付給FastCGI程序來執行,然後將得到的結果返回給瀏覽器。這種方式可以讓HTTP伺服器專一地處理靜態請求,或者將動態指令碼伺服器的結果返回給客戶端,這在很大程度上提高了整個應用系統的效能

Web Server啟動時載入FastCGI程序管理器(Apache Module或IIS ISAPI等)
FastCGI程序管理器自身初始化,啟動多個CGI直譯器程序(可建多個php-cgi),並等待來自Web Server的連線。
當客戶端請求到達Web Server時,FastCGI程序管理器選擇並連線到一個CGI直譯器。Web server將CGI環境變數和標準輸入傳送到FastCGI子程序php-cgi。
FastCGI子程序完成處理後,將標準輸出和錯誤資訊從同一連線返回Web Server。當FastCGI子程序關閉連線時,請求便告處理完成。FastCGI子程序接著等待,並處理來自FastCGI程序管理器(執行在Web Server中)的下一個連線。 在CGI模式中,php-cgi在此便退出了。
FastCGI與CGI特點:

對於CGI來說,每一個Web請求PHP都必須重新解析php.ini、重新載入全部擴充套件,並重新初始化全部資料結構。而使用FastCGI,所有這些都只在程序啟動時發生一次。一個額外的好處是,持續資料庫連線(Persistent database connection)可以工作。
由於FastCGI是多程序,所以比CGI多執行緒消耗更多的伺服器記憶體,php-cgi直譯器每程序消耗7至25兆記憶體,將這個數字乘以50或100就是很大的記憶體數。

2.4 PHP-FPM介紹

要了解PHP-FPM,就得先說說PHP-CGI。

PHP-CGI就是PHP實現的自帶的FastCGI管理器。 雖然是php官方出品,但是這丫的卻一點也不給力,效能太差,而且也很麻煩不人性化,主要體現在:

php-cgi變更php.ini配置後,需重啟php-cgi才能讓新的php-ini生效,不可以平滑重啟。
直接殺死php-cgi程序,php就不能運行了。
上面2個問題,一直讓很多人病垢了很久,所以很多人一直還是在用 Module 方式。 直到 2004年一個叫 Andrei Nigmatulin的屌絲髮明瞭PHP-FPM ,這神器的出現就徹底打破了這種局面,這是一個PHP專用的 fastcgi 管理器,它很爽的克服了上面2個問題,而且,還表現在其他方面更表現強勁。

也就是說,PHP-FPM 是對於 FastCGI 協議的具體實現,他負責管理一個程序池,來處理來自Web伺服器的請求。目前,PHP5.3版本之後,PHP-FPM是內置於PHP的。

因為PHP-CGI只是個CGI程式,他自己本身只能解析請求,返回結果,不會程序管理。所以就出現了一些能夠排程 php-cgi 程序的程式,比如說由lighthttpd分離出來的spawn-fcgi。同樣,PHP-FPM也是用於排程管理PHP解析器php-cgi的管理程式。

PHP-FPM通過生成新的子程序可以實現php.ini修改後的平滑重啟。

2.5 PHP執行模式總結

最後,我們來總結一下,這些技術經過不斷的升級,可以解決什麼問題(不然也不會升級嘛),所以,如果要搭建一個高效能的PHP WEB伺服器,目前最佳的方式是Apache/Nginx + FastCGI + PHP-FPM(+PHP-CGI)方式了,不要再使用 Module載入或者 CGI 方式)

主要資料