Apache運行機制剖析

目錄(?)[+]

Apache運行機制剖析:

1. B/S交互過程

瀏覽器(Browser)和服務器(Web Server)的交互過程:

 

1、  瀏覽器向服務器發出HTTP請求(Request)。

2、  服務器收到瀏覽器的請求數據,經過分析處理,向瀏覽器輸出響應數據(Response)。

3、  瀏覽器收到服務器的響應數據,經過分析處理,將最終結果顯示在瀏覽器中。

 下圖是一份瀏覽器請求數據和服務器響應數據的快照:

 

關於瀏覽器和服務器數據交互過程非常簡單,很容易理解。我想從事Web開發的人員都很清楚,在此不再贅述,僅供參考。

2. Apache概述

Apache是目前世界上使用最爲廣泛的一種Web Server,它以跨平臺、高效和穩定而聞名。按照去年官方統計的數據,Apache服務器的裝機量佔該市場60%以上的份額。尤其是在 X(Unix/Linux)平臺上,Apache是最常見的選擇。其它的Web Server產品,比如IIS,只能運行在Windows平臺上,是基於微軟.Net架構技術的不二選擇。

Apache並不是沒有缺點,它最爲詬病的一點就是變得越來越重,被普遍認爲是重量級的 WebServer。所以,近年來又涌現出了很多輕量級的替代產品,比如lighttpd,nginx等等,這些WebServer的優點是運行效率很 高,但缺點也很明顯,成熟度往往要低於Apache,通常只能用於某些特定場合,。


3. Apache組件邏輯圖

Apache是基於模塊化設計的,總體上看起來代碼的可讀性高於php的代碼,它的核心代碼 並不多,大多數的功能都被分散到各個模塊中,各個模塊在系統啓動的時候按需載入。你如果想要閱讀Apache的源代碼,建議你直接從main.c文件讀 起,系統最主要的處理邏輯都包含在裏面。MPM(Multi -Processing Modules,多重處理模塊)是Apache的核心組件之 一,Apache通過MPM來使用操作系統的資源,對進程和線程池進行管理。Apache爲了能夠獲得最好的運行性能,針對不同的平臺 (Unix/Linux、Window)做了優化,爲不同的平臺提供了不同的MPM,用戶可以根據實際情況進行選擇,其中最常使用的MPM有 prefork和worker兩種。至於您的服務器正以哪種方式運行,取決於安裝Apache過程中指定的MPM編譯參數,在X系統上默認的編譯參數爲 prefork。由於大多數的Unix都不支持真正的線程,所以採用了預派生子進程(prefork)方式,象Windows或者Solaris這些支持 線程的平臺,基於多進程多線程混合的worker模式是一種不錯的選擇。對此感興趣的同學可以閱讀有關資料,此處不再多講。Apache中還有一個重要的 組件就是APR(Apache portable Runtime Library),即Apache可移植運行庫,它是一個對操作系統調用的抽象庫,用來實現Apache內部組件對操作系統的使用,提高系統的可移植性。 Apache對於php的解析,就是通過衆多Module中的php Module來完成的。

 

Apache的邏輯構成以及與操作系統的關係

4. Apache的生命週期

這一節的內容會與php模塊的載入有關,您可以略微關注一下。以下是Apache的生命週期(prefork模式)示意圖。

5. Apache的兩種工作模式

Apache服務的兩種工作模式:prefork和worker

prefork的工作原理及配置

      1)工作原理:
    一個單獨的控制進程(父進程)負責產生子進程,這些子進程用於監聽請求並作出應答。Apache總是試圖保持一些備用的 (spare)或是空閒的子進程用於迎接即將到來的請求。這樣客戶端就無需在得到服務前等候子進程的產生。在Unix系統中,父進程通常以root身份運行以便邦定80端口,而 Apache產生的子進程通常以一個低特權的用戶運行。User和Group指令用於配置子進程的低特權用戶。運行子進程的用戶必須要對他所服務的內容有讀取的權限,但是對服務內容之外的其他資源必須擁有儘可能少的權限。
  2)  配置說明:

      如果不用“--with-mpm”顯式指定某種MPM,prefork就是Unix平臺上缺省的MPM。它所採用的預派生子進程方式也是Apache 1.3中採用的模式。prefork本身並沒有使用到線程,2.0版使用它是爲了與1.3版保持兼容性;另一方面,prefork用單獨的子進程來處理不同的請求,進程之間是彼此獨立的,這也使其成爲最穩定的MPM之一。

  若使用prefork,在make編譯和make install安裝後,使用“httpd -l”來確定當前使用的MPM,應該會看到prefork.c(如果看到worker.c說明使用的是worker MPM,依此類推)。再查看缺省生成的httpd.conf配置文件,裏面包含如下配置段:

  1. <IfModule prefork.c>  
  2. StartServers 5  
  3. MinSpareServers 5  
  4. MaxSpareServers 10  
  5. MaxClients 150  
  6. MaxRequestsPerChild 0  
  7. </IfModule>  

  prefork的具體工作原理是,控制進程在最初建立“StartServers”個子進程後,爲了滿足MinSpareServers設置的需要創建一個進程,等待一秒鐘,繼續創建兩個,再等待一秒鐘,繼續創建四個……如此按指數級增加創建的進程數,最多達到每秒32個,直到滿足MinSpareServers設置的值爲止。這就是預派生(prefork)的由來。這種模式可以不必在請求到來時再產生新的進程,從而減小了系統開銷以增加性能。

      當併發量請求數到達MaxClients(如256)時,而空閒進程只有10個。apache爲繼續增加創建進程。直到進程數到達256個。

     當併發量高峯期過去了,併發請求數可能只有一個時,apache逐漸刪除進程,直到進程數到達MaxSpareServers爲止

      StartServers:指定服務器啓動時建立的子進程數量,prefork默認爲5。

    MinSpareServers :指定空閒子進程的最小數量,默認爲5。假如當前空閒子進程數少於MinSpareServers ,那麼Apache將以最大每秒一個的速度產生新的子進程。此參數不要設的太大。

  MaxSpareServers:設置了最大的空閒進程數,默認爲10。如果空閒進程數大於這個值,Apache父進程會自動kill掉一些多餘子進程。這個值不要設得過大,但如果設的值比MinSpareServers小,Apache會自動把其調整爲MinSpareServers+1。如果站點負載較大,可考慮同時加大MinSpareServers和MaxSpareServers。

  MaxRequestsPerChild:設置的是每個子進程可處理的請求數。每個子進程在處理了“MaxRequestsPerChild”個請求後將自動銷燬。0意味着無限,即子進程永不銷燬。雖然缺省設爲0可以使每個子進程處理更多的請求,但如果設成非零值也有兩點重要的好處:

  ◆ 可防止意外的內存泄漏;

  ◆ 在服務器負載下降的時侯會自動減少子進程數。

  因此,可根據服務器的負載來調整這個值。個人認爲10000左右比較合適。

  MaxClients:是這些指令中最爲重要的一個,設定的是Apache可以同時處理的請求,是對Apache性能影響最大的參數。

       其缺省值150是遠遠不夠的,如果請求總數已達到這個值(可通過ps -ef|grep http|wc -l來確認),那麼後面的請求就要排隊,直到某個已處理請求完畢。這就是系統資源還剩下很多而HTTP訪問卻很慢的主要原因。系統管理員可以根據硬件配置和負載情況來動態調整這個值。

       雖然理論上這個值越大,可以處理的請求就越多,但在Apache1.3默認的最大隻能設置爲256(這是個硬限制)。如果把這個值設爲大於256,那麼Apache將無法起動。事實上,256對於負載稍重的站點也是不夠的。如果要加大這個值,必須在“configure”前手工修改的源代碼樹下的src/include/httpd.h中查找256,就會發現“#define HARD_SERVER_LIMIT 256”這行。把256改爲要增大的值(如4000),然後重新編譯Apache即可。

       但在Apache 2.0中,新加入了ServerLimit指令,可以突破最大請求數爲256的限制。 使得無須重編譯Apache就可以加大MaxClients。下面是prefork配置段:

  1. <IfModule prefork.c>  
  2. ServerLimit 2000  
  3. StartServers 10  
  4. MinSpareServers 10  
  5. MaxSpareServers 15  
  6. MaxClients 1000  
  7. MaxRequestsPerChild 10000  
  8. </IfModule>  

  ServerLimit:上述配置中,ServerLimit的最大值是2000,對於大多數站點已經足夠。如果一定要再加大這個數值,對位於源代碼樹下server/mpm/prefork/prefork.c中以下兩行做相應修改即可:

  1. #define DEFAULT_SERVER_LIMIT 256  
  2. #define MAX_SERVER_LIMIT 2000  

     此時必須 MaxClients ≤ ServerLimit ≤ 2000. 即prefork的默認併發量最大是2000。

     ServerLimit 效前提:必須放在其他指令的前面,同時要想改變這個硬限制必須完全停止服務器然後再啓動服務器(直接重啓是不行的)。

worker的工作原理及配置

  工作原理:每個進程能夠擁有的線程數量是固定的。服務器會根據負載情況增加或減少進程數量。一個單獨的控制進程(父進程)負責子進程的建立。每個子進程能夠建立ThreadsPerChild數量的服務線程和一個監聽線程,該監聽線程監聽接入請求並將其傳遞給服務線程處理和應答。Apache總是試圖維持一個備用(spare)或是空閒的服務線程池。這樣,客戶端無須等待新線程或新進程的建立即可得到處理。在Unix中,爲了能夠綁定80端口,父進程一般都是以root身份啓動,隨後,Apache以較低權限的用戶建立子進程和線程。User和Group指令用於配置Apache子進程的權限。雖然子進程必須對其提供的內容擁有讀權限,但應該儘可能給予他較少的特權。另外,除非使用了suexec ,否則,這些指令配置的權限將被CGI腳本所繼承。

        相對於prefork,worker是2.0 版中全新的支持多線程和多進程混合模型的MPM。由於使用線程來處理,所以可以處理相對海量的請求,而系統資源的開銷要小於基於進程的服務器。但是,worker也使用了多進程,每個進程又生成多個線程,以獲得基於進程服務器的穩定性。這種MPM的工作方式將是Apache 2.0的發展趨勢。

  在configure -with-mpm=worker後,進行make編譯、make install安裝。在缺省生成的httpd.conf中有以下配置段:

  1. <IfModule worker.c>  
  2. StartServers 2  
  3. MaxClients 150  
  4. MinSpareThreads 25  
  5. MaxSpareThreads 75  
  6. ThreadsPerChild 25  
  7. MaxRequestsPerChild 0  
  8. </IfModule>  

  worker的工作原理是:由主控制進程生成“StartServers”個子進程,每個子進程中包含固定的ThreadsPerChild線程數,各個線程獨立地處理請求。同樣,爲了不在請求到來時再生成線程,MinSpareThreads和MaxSpareThreads設置了最少和最多的空閒線程數;而MaxClients設置了所有子進程中的線程總數。如果現有子進程中的線程總數不能滿足負載,控制進程將派生新的子進程。

     StartServers服務器啓動時建立的子進程數,默認值是"3"。

    ServerLimit服務器允許配置的進程數上限。這個指令和ThreadLimit結合使用配置了MaxClients最大允許配置的數值。任何在重啓期間對這個指令的改變都將被忽略,但對MaxClients的修改卻會生效。

    MinSpareThreads :最小空閒線程數,默認值是"75"。這個MPM將基於整個服務器監控空閒線程數。假如服務器中總的空閒線程數太少,子進程將產生新的空閒線程。

    MaxSpareThreads:配置最大空閒線程數。默認值是"250"。這個MPM將基於整個服務器監控空閒線程數。假如服 務器中總的空閒線程數太多,子進程將殺死多餘的空閒線程。MaxSpareThreads的取值範圍是有限制的。Apache將按照如下限制自動修正您配置的值:worker需要其大於等於MinSpareThreads加上ThreadsPerChild的和

  MinSpareThreads和MaxSpareThreads這兩個參數對Apache的性能影響並不大,可以按照實際情況相應調節。

       ThreadLimit:每個子進程可配置的線程數上限。這個指令配置了每個子進程可配置的線程數ThreadsPerChild上限。任何在重啓期間對這個指令的改變都將被忽略,但對ThreadsPerChild的修改卻會生效。默認值是"64".

  ThreadsPerChild:是worker MPM中與性能相關最密切的指令。ThreadsPerChild的最大缺省值是64如果負載較大,64也是不夠的。這時要顯式使用ThreadLimit指令,它的最大缺省值是20000。上述兩個值位於源碼樹server/mpm/worker/worker.c中的以下兩行:

  1. #define DEFAULT_THREAD_LIMIT 64  
  2. #define MAX_THREAD_LIMIT 20000  

  這兩行對應着ThreadsPerChild和ThreadLimit的限制數。最好在configure之前就把64改成所希望的值。注意,不要把這兩個值設得太高,超過系統的處理能力,從而因Apache不起動使系統很不穩定。

  Worker模式下所能同時處理的請求總數是由子進程總數乘以ThreadsPerChild值決定的,應該大於等於MaxClients。如果負載很大,現有的子進程數不能滿足時,控制進程會派生新的子進程。默認最大的子進程總數是16,加大時也需要顯式聲明ServerLimit(最大值是20000)。這兩個值位於源碼樹server/mpm/worker/worker.c中的以下兩行:

  1. #define DEFAULT_SERVER_LIMIT 16  
  2. #define MAX_SERVER_LIMIT 20000  

  需要注意的是,如果顯式聲明瞭ServerLimit,那麼它乘以ThreadsPerChild的值必須大於等於MaxClients,而且MaxClients必須是ThreadsPerChild的整數倍,否則Apache將會自動調節到一個相應值(可能是個非期望值)。下面是worker配置段:

  1. <IfModule worker.c>  
  2. StartServers 3  
  3. MaxClients 2000  
  4. ServerLimit 25  
  5. MinSpareThreads 50  
  6. MaxSpareThreads 200  
  7. ThreadLimit 200  
  8. ThreadsPerChild 100  
  9. MaxRequestsPerChild 0  
  10. </IfModule>  

  通過上面的敘述,可以瞭解到Apache 2.0中prefork和worker這兩個重要MPM的工作原理,並可根據實際情況來配置Apache相關的核心參數,以獲得最大的性能和穩定性。


     MaxClients:允許同時伺服的最大接入請求數量(最大線程數量)。任何超過MaxClients限制的請求都將進入等候 隊列。默認值是"400",16 (ServerLimit)乘以25(ThreadsPerChild)的結果。因此要增加MaxClients的時候,您必須同時增加 ServerLimit的值。

    ThreadsPerChild:每個子進程建立的常駐的執行線程數。默認值是25。子進程在啓動時建立這些線程後就不再建立新的線程了。

    MaxRequestsPerChild:配置每個子進程在其生存期內允許伺服的最大請求數量。到達MaxRequestsPerChild的限制後,子進程將會結束。假如MaxRequestsPerChild爲"0",子進程將永遠不會結束。
     將MaxRequestsPerChild配置成非零值有兩個好處:
     1.能夠防止(偶然的)內存泄漏無限進行,從而耗盡內存。 
     2.給進程一個有限壽命,從而有助於當服務器負載減輕的時候減少活動進程的數量。 
     注意
     對於KeepAlive鏈接,只有第一個請求會被計數。事實上,他改變了每個子進程限制最大鏈接數量的行爲。

6.Apache的運行

Apache運行分爲啓動階段和運行階段。

5.1.    啓動階段

在啓動階段,Apache主要進行配置文件解析(例如http.conf以及Include指令設定的配置文件等)、模塊加載(例如mod_php.so,mod_perl.so等)和系統資源初始化(例如日誌文件、共享內存段等)工作。

在這個階段,Apache爲了獲得系統資源最大的使用權限,將以特權用戶root(X系統)或超級管理員administrator(Windows系統)完成啓動。


Apache和“php處理機”的裝配過程就是在這個階段完成的。

“php處理機”就是負責解釋和執行你的php代碼的系統模塊。這個名字是我特意創造的,目的是爲了幫助你理解本節的內容,後面的章節還會給出更專業的名稱。


你單獨做過php的安裝配置嗎?

如果你做過類似的工作,下面的內容很容易理解;如果你沒有做過,可以嘗試安裝一下,有助於加深你的理解。不過,我的文章向來深入淺出,我會盡量把這個過程講得更淺顯一些。其實php的安裝非常簡單,如果你很感興趣的話,可以到網上隨便搜一篇安裝指南,按步驟照做就可以了。

把php最終集成到Apache系統中,還需要對Apache進行一些必要的設置。這裏,我們就以php的mod_php5 SAPI運行模式爲例進行講解,至於SAPI這個概念後面我們還會詳細講解。

假定我們安裝的版本是Apache2 和 Php5,那麼需要編輯Apache的主配置文件http.conf,在其中加入下面的幾行內容:

Unix/Linux環境下:

LoadModule php5_module modules/mod_php5.so

AddType application/x-httpd-php .php

注:其中modules/mod_php5.so 是X系統環境下mod_php5.so文件的安裝位置。

 

Windows環境下:

LoadModule php5_module d:/php/php5apache2.dll

AddType application/x-httpd-php .php

注:其中d:/php/php5apache2.dll 是在Windows環境下php5apache2.dll文件的安裝位置。

這兩項配置就是告訴Apache Server,以後收到的Url用戶請求,凡是以php作爲後綴,就需要調用php5_module模塊(mod_php5.so/ php5apache2.dll)進行處理。

 

這個過程可以參考以下的示意圖:

Apache啓動階段的源碼包含在server/main.c中,我整理了一下源碼中的對應關係:

不熟悉unix/linux的同學可能會問so文件(mod_php5.so)是個什麼樣的文件?

unix/linux下,so後綴文件是一個DSO文件,DSO與windows系統下的dll是等價概念,都是把一堆函數封裝在一個二進制文件中。調用它們的進程把它們裝入內存後,會將其映射到自己的地址空間。

DSO全稱爲Dynamic Shared Object,即動態共享對象。DLL全稱爲Dynamic Link Library 即動態鏈接庫。

Apache 服務器的體系結構的最大特點,就是高度模塊化。如果你爲了追求處理效率,可以把這些dso模塊在apache編譯的時候靜態鏈入,這樣會提高Apache 5%左右的處理性能。

5.2、運行階段

     5.2.1 運行階段概述

    在運行階段,Apache主要工作是處理用戶的服務請求。

    在這個階段,Apache放棄特權用戶級別,使用普通權限,這主要是基於安全性的考慮,防止由於代碼的缺陷引起的安全漏洞。象微軟的IIS就曾遭受“紅色代碼(Code Red)”和“尼姆達(Nimda)”等惡意代碼的溢出攻擊。

     2.2 運行階段流程
    Apache將請求處理循環分爲11個階段,依次是:Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp。

 

     Apache Hook機制

     Apache的Hook機制是指:Apache 允許模塊(包括內部模塊和外部模塊,例如mod_php5.so,mod_perl.so等)將自定義的函數注入到請求處理循環中。換句話說,模塊可以在 Apache的任何一個處理階段中掛接(Hook)上自己的處理函數,從而參與Apache的請求處理過程。


    mod_php5.so/ php5apache2.dll就是將所包含的自定義函數,通過Hook機制注入到Apache中,在Apache處理流程的各個階段負責處理php請求。

 

    關於Hook機制在Windows系統開發也經常遇到,在Windows開發既有系統級的鉤子,又有應用級的鉤子。常見的翻譯軟件(例如金山詞霸等等)的 屏幕取詞功能,大多數是通過安裝系統級鉤子函數完成的,將自定義函數替換gdi32.dll中的屏幕輸出的繪製函數。

 

    Apache請求處理循環詳解
    Apache請求處理循環的11個階段都做了哪些事情呢?
   

    1、Post-Read-Request階段

    在正常請求處理流程中,這是模塊可以插入鉤子的第一個階段。對於那些想很早進入處理請求的模塊來說,這個階段可以被利用。

    2、URI Translation階段
    Apache在本階段的主要工作:將請求的URL映射到本地文件系統。模塊可以在這階段插入鉤子,執行自己的映射邏輯。mod_alias就是利用這個階段工作的。

    3、Header Parsing階段
    Apache在本階段的主要工作:檢查請求的頭部。由於模塊可以在請求處理流程的任何一個點上執行檢查請求頭部的任務,因此這個鉤子很少被使用。mod_setenvif就是利用這個階段工作的。

 

    4、Access Control階段
    Apache在本階段的主要工作:根據配置文件檢查是否允許訪問請求的資源。Apache的標準邏輯實現了允許和拒絕指令。mod_authz_host就是利用這個階段工作的。

    5、Authentication階段
     Apache在本階段的主要工作:按照配置文件設定的策略對用戶進行認證,並設定用戶名區域。模塊可以在這階段插入鉤子,實現一個認證方法。

 

    6、Authorization階段
    Apache在本階段的主要工作:根據配置文件檢查是否允許認證過的用戶執行請求的操作。模塊可以在這階段插入鉤子,實現一個用戶權限管理的方法。

    7、MIME Type Checking階段
    Apache在本階段的主要工作:根據請求資源的MIME類型的相關規則,判定將要使用的內容處理函數。標準模塊mod_negotiation和mod_mime實現了這個鉤子。

    8、FixUp階段
    這是一個通用的階段,允許模塊在內容生成器之前,運行任何必要的處理流程。和Post_Read_Request類似,這是一個能夠捕獲任何信息的鉤子,也是最常使用的鉤子。

    9、Response階段
    Apache在本階段的主要工作:生成返回客戶端的內容,負責給客戶端發送一個恰當的回覆。這個階段是整個處理流程的核心部分。

    10、Logging階段
    Apache在本階段的主要工作:在回覆已經發送給客戶端之後記錄事務。模塊可能修改或者替換Apache的標準日誌記錄。

 

    11、CleanUp階段
    Apache在本階段的主要工作:清理本次請求事務處理完成之後遺留的環境,比如文件、目錄的處理或者Socket的關閉等等,這是Apache一次請求處理的最後一個階段。

 

    模塊的注入Apache的過程可以參考源碼中server/core.c文件:
 

    mod_php5.so/ php5apache2.dll注入到Apache的函數中,最重要的就是Response階段的處理函數。


6. Apache的性能調優

假如apache的配置

<IfModule prefork.c>
    StartServers         8
    MinSpareServers      5
    MaxSpareServers      20
    MaxClients           256
    MaxRequestsPerChild  4000
</IfModule>

在沒有啓用serverlLimit的情況下,使用ab測試:

ab -n 10000 -c 1000  http://192.168.1.191/test.php


如果繼續保持同樣的併發量繼續測試(測試完後馬上繼續測試),由於apache的大部分子進程還沒有被kill掉,創建子進程的時間就是少了,即有部分子進程已經預派生出來了。即n=20000(ab -n 20000-c 1000  http://192.168.1.191/test.php)時,時間應該不是137 *2 = 274S.


如果過一段時間在測試,即等apache kill進程,直到進程數等於MaxSpareServers (20)再測試,時間還是差不多的(137s左右)。

如果n=40000(ab -n 40000 -c 1000  http://192.168.1.191/test.php)觀察服務器 apache的進程數:ps -ef | grep httpd | wc -l 。當apache的進程數到達256個後不再增加。

如果配置serverLimit

<IfModule prefork.c>

    serverLimit          1000

    StartServers         8
    MinSpareServers      5
    MaxSpareServers      20
    MaxClients           1000
    MaxRequestsPerChild  4000
</IfModule>

apache的進程就可以突破256的限制了。

調優設置:

      1)如果服務器只是的某個時段的併發量很多,設置serverLimit ,並修改MaxClients  。

      2)如果服務器持續性地高負載可考慮同時加大MinSpareServers和MaxSpareServers。

發佈了45 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章