問題來源
如果訪問量比較小,其實什麼配置都沒問題。在一些特殊場景下,例如促銷活動,訪問量會比較集中。峯值差不多能達到每秒1000到2000次的訪問,而且還在繼續增長中。
我們一直判斷是數據庫卡死的問題,後來發現,原來了服務器掛了。進一步定位,發現是apache接收了太多了請求,起了無數進程,內存用爆,機器卡死了。
如何配置apache,最大程度的發揮系統性能呢?
apache2三種模式
從2.0開始,apache引入了MPM(Multi-Processing Module,多進程處理模塊)。MPM有prefork, worker和event(在2.4版本中穩定發佈)模式,三種模式擁有不同的特點和性能。
prefork MPM
prefork是比較古老而又穩定的apache模式,特點是每個進程都是單線程,在一個時間點只能處理一個連接,需要啓動大量的進程來處理高併發的請求。由於是單線程進程,因而無須考慮線程安全的問題,可以使用非線程安全的庫,例如mod_php。
優點是成熟穩定,缺點是比較消耗內存,而且併發支持受限於進程數量,對高併發支持稍差。
worker MPM
worker同樣使用多個進程,但每個進程又擁有多個線程,每個線程處理一個連接。由於線程是輕量級的,因而具有較高的併發性,同時,多個進程又獲得了一定的穩定性。
worker模式特點是佔用內存少,併發性比較高,缺點是必須考慮線程安全。如果使用了keep-alive方式,一個線程可能會被一直保持一個連接,但中間沒有請求,直到超時。如果有多個線程被這樣佔據,在高併發場景下同樣會出現無線程可用的情形。
event MPM
event模式是在2.4版本中才穩定發佈的模式,它在worker的基礎上,解決了keep-alive連接不能釋放的問題。event MPM中,會有一個專門的線程來管理這些keep-alive類型的線程,當有真實請求過來的時候,將請求傳遞給服務線程,執行完畢後,又允許它釋放。這樣增強了高併發場景下的請求處理能力。
apache2 配置
apache服務器的性能,在很大程度上取決於參數配置。
首先,我們如何查看當前apache所採用的模式呢?可以通過下面的命令:
apachectl -V | grep -i mpm
接下來,針對相應的模式,進行配置。apache2的默認配置文件位於/etc/apache2/apache2.conf,不同模式對應的配置文件在/etc/apache2/mods-available/下,有mpm_prefork.conf, mpm_worker.conf和mpm_event.conf。
prefork MPM
mpm_prefork.conf文件內容如下:
<IfModule mpm_prefork_module>
StartServers 10 # 啓動時進程數
MinSpareServers 5 # 最小空閒進程數
MaxSpareServers 10 # 最大空閒進程數
MaxRequestWorkers 100 # 最大併發進程數
MaxConnectionsPerChild 10000 # 最大連接數限制
</IfModule>
各個指令的含義:
StartServers : apache2啓動時創建的服務進程數量。
MinSpareServers:最小空閒進程數量,空閒進程指的是沒有處理請求的進程。
MaxSpareServers : 最大空閒進程數量。
MaxRequestWorkers : 最大同時處理請求的進程數量,也是最大的同時連接數,表示了apache的最大請求併發能力,超過該數目後的請求,將排隊。
MaxConnectionsPerChild : 進程生命週期內,處理的最大請求數目。達到該數目後,進程將死掉。如果設置爲0,表示沒有限制。該參數的意義在於,避免了可能存在的內存泄露帶來的系統問題。
通過上面的介紹可以發現,prefork模式下,影響併發性能最重要的參數就是MaxRequestWorkers,它決定了apache的併發處理能力。但是這個參數不是越大越好,因爲如果超出了系統硬件能力,機器會卡死。
如果確定合適的MaxRequestWorkers呢?
首先,通過top命令查看apache進程佔用的資源,主要看%CPU和%MEM這兩個指標,例如,每個進程的CPU佔用率不超過1%,每個進程的內存佔用率不超過2%,考慮內存限制,比較合適的apache進程數量爲50個。
然後,逐步測試最大值。通過觀測得來的CPU和內存的指標有一定的誤差,一般可以適當調節這個數值,例如調到1.5或者2倍,再通過峯值場景下的機器是否卡頓來判斷是繼續上調還是下調。
調整完參數後,一般需要重啓apache。
service apache2 restart
worker MPM
mpm_worker.conf文件內容如下:
<IfModule mpm_worker_module>
StartServers 2 # 啓動時進程數
MinSpareThreads 25 # 最小空閒線程數
MaxSpareThreads 75 # 最大空閒線程數
ThreadLimit 64 # 每個進程可以啓動的線程數量上限值
ThreadsPerChild 25 # 每個進程可以啓動的線程數量
MaxRequestWorkers 400 # 線程數量最大值
MaxConnectionsPerChild 0 # 最大連接數限制
</IfModule>
配置文件和前面的prefork有一些變動,但是基本的概念差不多。原來以進程爲單位的概念轉變成了以線程爲單位的概念,例如MinSpareServers轉變爲MinSpareThreads,多了一個ThreadsPerChild,表示每個進程可以有的線程數量,其上限值爲ThreadLimit。
注意,修改了ThreadsPerChild後可以通過apache2 restart來生效,但是修改了ThreadLimit只能先stop,然後start。
在worker模式中,MaxRequestWorkers轉變爲了線程數量,比起prefork模式下可以有比較大的增長,通常可以有好幾倍的差別。這是worker模式相比於prefork的優勢,worker擁有比較多的處理線程,可以同時保持大量的連接,峯值應對能力比較強。儘管出於CPU或者數據庫的原因,這些連接不能在第一時間內被處理,可能產生較大延遲。
如果確定worker模式的參數呢?
有兩個參數比較重要是,一是ThreadsPerChild,這個表示每個進程的線程數目,一般採用默認值即可,如果是併發量比較大,可以考慮加大這個值。另外一個就是MaxRequestWorkers了,在workers模式下,任務處理的負載會分發給php-fpm來處理,所以apache主要承擔了建立和保持連接的任務,並不消耗太多資源,因而在理論上,MaxRequestWorkers可以非常大。
但是從實用體驗來看,如果MaxRequestWorkers遠超系統的請求處理能力,會有大量的請求排隊,因而產生比較大的延遲,這個時候的體驗並不好。所以這個參數一開始可以稍大,例如可以設置爲prefork模式下的幾倍,然後根據線上峯值延遲不斷調整這個數值。
MaxRequestWorkers必須是ThreadsPerChild的整數倍,否則apache會提示調整到一個相近的值。MaxRequestWorkers也有最大值限制,即ServerLimit乘以ThreadsPerChild。默認ServerLimit值是16,必要時可以顯式聲明加大這個值。
調整php-fpm
worker模式下的PHP一般運行在fastcgi環境下,所以決定系統性能的其實是PHP的處理能力。默認的php-fpm配置文件位於/etc/php5/fpm/pool.d/www.conf。通常有以下選項可以配置:
pm = dynamic # PHP進程管理方式,動態
pm.max_children = 32 # 最大PHP處理進程數
pm.start_servers = 2 # 啓動時PHP進程數目
pm.min_spare_servers = 4 # 最小的空閒PHP進程數
pm.max_spare_servers = 8 # 最大的空閒PHP進程數
pm.max_requests = 5000 # 進程生命週期處理的最大請求數,避免內存泄露
同理,這裏比較關鍵的是max_children參數,它直接決定了PHP處理請求的能力。這個參數的選取,也需要根據每個php-fpm所佔用的CPU和內存來決定,同樣通過top命令查看php-fpm進程消耗的資源,然後計算一個大概值。在調整的過程中,可以結合top命令查看CPU和內存消耗的總量,只要有足夠剩餘,是可以加大這個數值的。調整完參數後,需要重啓php-fpm。
service php5-fpm restart
event MPM
event模式下的參數意義和worker模式完全一樣,按照上面的策略來調整即可。
event相比於worker的優勢是,它解決了worker模式下長連接線程的阻塞問題。
值得一提的是,worker/event模式的請求處理模式,已經和nginx的libevent模式相同了。在mod_php模式下,每個apache進程都需要直接執行php,所以很容易達到系統資源限制。但是在php-fpm模式下,apache只負責建立連接,然後把請求傳遞給php-fpm來處理。這樣,apache可以保持大量的連接,請求處理能力取決於php服務器的性能。
由於event是明顯優於worker的,所以在apache2.4及後續版本中,一般優先選擇event模式。
常見問題
查看apache的error日誌,可以發現許多系統運行中的問題。
server reached MaxRequestWorkers setting
[mpm_prefork:error] [pid 1134] AH00161: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
進程或者線程數目達到了MaxRequestWorkers,可以考慮增加這個值,當然,先考慮增加硬件。
scoreboard is full
[mpm_event:error] [pid 7555:tid 140058436118400] AH00485: scoreboard is full, not at MaxRequestWorkers
這個問題好像是apache2自帶的bug,我們無力解決。好在這個問題一般只會影響單個線程,所以暫時可以忍。
一些系統狀態指標
top命令
第一行裏面的load average可以查看系統負載情況。第三行可以查看CPU使用問題。第四行可以查看內存佔用及剩餘內存。
進程列表裏面,%CPU和%MEM顯示了每個進程所佔用的資源百分比。
apache日誌
訪問日誌默認位置/var/log/apache2/access.log,可以通過日誌文件查看每個請求處理結果及所用時間。
error日誌一般位於/var/log/apache2/error.log,可以查看apache運行中的一些error報告 。
ps命令
查看當前系統apache和php進程、線程數目,下面的命令適用於ubuntu系統。
查看apache進程。
ps aux | grep apache
查看apache進程數目
加上wc -l 命令即可。
ps aux | grep apache | wc -l
worker/event模式下,查看線程數目。
ps -eLf | grep apache | wc -l
查看php-fpm進程數。
ps aux | grep php-fpm | wc -l
apache性能調試是一個比較複雜的問題。除了apache本身配置、PHP配置,還會涉及到數據庫。後臺的難點就在於如何應對高併發、大流量的請求,這往往不是單個點可以解決的,需要系統各個模塊來協調。
參考
How do I select which Apache MPM to use?
PERFORMANCE OF APACHE 2.4 WITH THE EVENT MPM COMPARED TO NGINX
Determining the correct number of child processes for PHP-FPM on NGinx