關鍵要點
幾年前,Apache基金會的Web服務器(簡稱為“Apache”)非常普遍,以至於它成為“Web服務器”一詞的同義詞。它在Linux系統上的守護進程名為httpd(僅表示http進程),並且預裝在主要的Linux發行版中。
它最初於1995年發布,引用維基百科的話說,“它在萬維網的初期發展中發揮了關鍵作用”。根據W3techs的數據,它仍然是最常用的Web服務器軟件。然而,根據那些顯示過去十年的一些趨勢以及與其他解決方案的比較的報告,其市場份額正在下降。 Netcraft和Builtwith提供的報告略有不同,但都同意Apache的市場份額呈下降趨勢,而Nginx的市場份額正在增長。
Nginx(發音為engine x)由Igor Sysoev於2004年發布,其明確目標是超越Apache。 Nginx的網站有一篇值得閱讀的文章,比較了這兩種技術。起初,它主要用作Apache的補充,主要用於提供靜態文件,但隨著它不斷發展以處理各種Web服務器任務,它一直在穩步增長。
它通常用作反向代理、負載均衡器和HTTP緩存。 CDN和視頻流提供商使用它來構建其性能至關重要的內容交付系統。
Apache已經存在很長時間了,並且有很多模塊可以選擇。 Apache服務器的管理眾所周知的是用戶友好的。動態模塊加載允許編譯不同的模塊並添加到Apache堆棧中,而無需重新編譯主服務器二進製文件。通常,模塊將在Linux發行版存儲庫中,安裝它們後,可以通過系統包管理器優雅地使用類似a2enmod的命令將它們添加到堆棧中。 Nginx尚未看到這種靈活性。當我們查看設置Nginx以支持HTTP/2的指南時,模塊是Nginx需要構建的內容——在構建時配置的。
另一個促成Apache市場統治地位的特性是.htaccess文件。它是Apache的殺手鐧,使其成為共享託管環境的首選解決方案,因為它允許在目錄級別控制服務器配置。 Apache提供的服務器上的每個目錄都可以有自己的.htaccess文件。
Nginx不僅沒有等效的解決方案,而且由於性能影響而阻止這種用法。
1995-2005年服務器廠商市場份額。數據來自Netcraft
LiteSpeed或LSWS是一個服務器競爭者,其靈活性水平可與Apache相比,同時又不犧牲性能。它支持Apache風格的.htaccess、mod_security和mod_rewrite,並且值得考慮用於共享設置。它被計劃作為Apache的直接替代品,並且可以與cPanel和Plesk一起使用。自2015年以來,它一直支持HTTP/2。
LiteSpeed有三個許可層級:OpenLiteSpeed、LSWS Standard和LSWS Enterprise。 Standard和Enterprise附帶一個可選的緩存解決方案,可與Varnish媲美,即LSCache,它內置於服務器本身,並且可以使用.htaccess文件(每個目錄)中的重寫規則進行控制。它還內置了一些DDOS緩解“電池”。這加上其事件驅動架構,使其成為一個強大的競爭者,主要針對注重性能的託管提供商,但即使對於較小的服務器或網站,它也值得設置。
優化系統時,我們不能過分強調對硬件設置給予足夠的關注。無論我們為設置選擇哪種解決方案,擁有足夠的RAM都至關重要。當Web服務器進程或像PHP這樣的解釋器沒有足夠的RAM時,它們就會開始交換,而交換實際上意味著使用硬盤來補充RAM內存。這樣做的效果是每次訪問此內存時都會增加延遲。這讓我們想到了第二點——硬盤空間。使用快速的SSD存儲是網站速度的另一個關鍵因素。我們還需要注意CPU可用性以及服務器數據中心與目標受眾的物理距離。
要更深入地了解性能調整的硬件方面,Dropbox有一篇不錯的文章。
詳細監控當前服務器堆棧性能的一種實用方法是htop,它適用於Linux、Unix和macOS,並為我們的進程提供彩色概述。
其他監控工具包括New Relic(一個具有全套工具的高級解決方案)和Netdata(一個開源解決方案,提供出色的可擴展性、細粒度的指標和可定制的Web儀表板,適用於小型VPS系統和服務器網絡監控)。它可以通過電子郵件、Slack、pushbullet、Telegram、Twilio等發送任何應用程序或系統進程的警報。
Monit是另一個無頭開源工具,可以監控系統,並且可以配置為在滿足某些條件時向我們發出警報,或重新啟動某些進程,或重新啟動系統。
AB(Apache Benchmark)是Apache基金會的一個簡單的負載測試工具,Siege是另一個負載測試程序。本文解釋瞭如何設置它們,這裡有一些關於AB的更高級技巧,而對Siege的深入了解可以在這裡找到。
如果您更喜歡Web界面,那麼Locust是一個基於Python的工具,它對於測試網站性能非常方便。
安裝Locust後,我們需要在我們將從中啟動它的目錄中創建一個locustfile:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然後我們只需從命令行啟動它:
<code>locust --host=https://my-website.com </code>
關於這些負載測試工具的一個警告:它們具有DDoS攻擊的效果,因此建議您將測試限制在您自己的網站上。
Apache可以追溯到1995年和互聯網的早期,當時服務器運行的一種可接受方式是在每個傳入的TCP連接上生成一個新進程並對其進行回复。如果更多連接進來,則會創建更多工作進程來處理它們。生成新進程的成本很高,Apache開發人員設計了一種prefork模式,其中預先生成了一定數量的進程。每個進程中嵌入的動態語言解釋器(如mod_php)仍然成本很高,並且Apache的默認設置會導致服務器崩潰。每個進程只能處理一個傳入連接。
此模型在Apache的MPM(多進程模塊)系統中稱為mpm_prefork_module。根據Apache的網站,此模式幾乎不需要配置,因為它可以自我調節,並且最重要的是MaxRequestWorkers指令足夠大,可以處理您期望接收的盡可能多的同時請求,但又足夠小,以確保所有進程都有足夠的物理RAM。
一個小型Locust負載測試,顯示生成大量Apache進程以處理傳入流量。
我們可以補充一點,這種模式可能是Apache聲名狼藉的主要原因。它可能會資源效率低下。
Apache的2.0版本帶來了另外兩個試圖解決prefork模式問題的MPM。它們是worker模塊或mpm_worker_module和event模塊。
Worker模塊不再基於進程;它是一種混合的基於進程線程的操作模式。引用Apache的網站:
單個控制進程(父進程)負責啟動子進程。每個子進程根據ThreadsPerChild指令中指定的數量創建固定數量的服務器線程,以及一個偵聽線程,該線程偵聽連接並在連接到達時將它們傳遞給服務器線程進行處理。
此模式更節省資源。
Apache的2.4版本為我們帶來了第三個MPM——event模塊。它基於worker MPM,並添加了一個單獨的偵聽線程,該線程在HTTP請求完成後管理休眠的keepalive連接。它是一種非阻塞異步模式,內存佔用更小。更多關於2.4版本改進的信息在這裡。
我們在虛擬服務器上加載了一個大約有1200個帖子的測試WooCommerce安裝程序,並在Apache 2.4上使用默認的prefork模式和mod_php對其進行了測試。
首先,我們在https://tools.pingdom.com上使用libapache2-mod-php7和mpm_prefork_module對其進行了測試:
然後,我們對event MPM模塊進行了測試。
我們必須將multiverse添加到我們的/etc/apt/sources.list中:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然後我們執行sudo apt-get update並安裝libapache2-mod-fastcgi和php-fpm:
<code>locust --host=https://my-website.com </code>
由於php-fpm是與Apache分離的服務,因此需要重新啟動:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
然後我們禁用了prefork模塊,並啟用了event模式和proxy_fcgi:
<code>sudo apt-get install libapache2-mod-fastcgi php7.0-fpm </code>
我們將此代碼段添加到我們的Apache虛擬主機中:
<code>sudo service start php7.0-fpm </code>
此端口需要與/etc/php/7.0/fpm/pool.d/www.conf中的php-fpm配置一致。更多關於php-fpm設置的信息在這裡。
然後我們在/etc/apache2/mods-available/mpm_event.conf中調整了mpm_event配置,記住我們對此測試的迷你VPS資源受到限制——因此我們只是減少了一些默認數字。 Apache網站上每個指令的詳細信息,以及此處關於event mpm的技巧。請記住,啟動的服務器會消耗一定量的內存,無論它們有多忙。 MaxRequestWorkers指令設置允許的同時請求的數量:將MaxConnectionsPerChild設置為非零值很重要,因為它可以防止可能的內存洩漏。
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然後我們使用sudo service apache2 restart重新啟動服務器(如果我們更改某些指令,例如ThreadLimit,則需要顯式停止和啟動服務,使用sudo service apache2 stop; sudo service apache2 start)。
我們現在在Pingdom上的測試顯示頁面加載時間減少了一半以上:
禁用.htaccess:htaccess允許設置服務器根目錄中每個目錄的特定配置,而無需重新啟動。因此,在每次請求時遍歷所有目錄以查找.htaccess文件會產生性能損失。
引自Apache文檔:
一般來說,只有在您無權訪問主服務器配置文件時才應使用.htaccess文件。 …一般來說,應盡可能避免使用.htaccess文件。您可以像在主服務器配置文件中使用
部分一樣有效地進行任何您認為要放在.htaccess文件中的配置。
解決方案是在/etc/apache2/apache2.conf中禁用它:
<code>locust --host=https://my-website.com </code>
如果我們需要它用於特定目錄,那麼我們可以在虛擬主機文件中的
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
其他技巧包括:
Nginx是一個事件驅動和非阻塞的Web服務器。引用Hacker News上的一位發帖人:
與事件循環相比,分叉進程非常昂貴。基於事件的HTTP服務器最終獲勝。
這個說法在Hacker News上引發了一場相當激烈的辯論,但根據我們的經驗,僅僅從mpm_prefork Apache切換到Nginx通常就意味著可以防止網站崩潰。簡單地切換到Nginx通常本身就是一種解決方法。
對Nginx架構更全面的視覺解釋可以在這裡找到。
Nginx建議將worker的數量固定為PC核心的數量(就像我們對Apache的mpm_event配置所做的那樣),通過在/etc/nginx/nginx.conf中將worker_processes設置為auto(默認為1 )。
worker_connections設置每個worker進程可以處理的連接數。默認為512,但通常可以增加。
Keepalive連接是影響性能的服務器方面,這在基準測試中通常不可見。
根據Nginx網站:
HTTP keepalive連接是必要的性能特性,可以減少延遲並加快網頁加載速度。
建立新的TCP連接可能代價高昂——更不用說涉及HTTPS加密的情況了。 HTTP/2協議通過其多路復用功能減輕了這種情況。重用現有連接可以減少請求時間。
Apache的mpm_prefork和mpm_worker存在並發限制,這與keepalive事件循環形成對比。這在Apache 2.4的mpm_event模塊中得到一定程度的修復,並且在Nginx中作為唯一默認的操作模式出現。 Nginx worker可以同時處理數千個傳入連接,如果它用作反向代理或負載均衡器,則Nginx使用本地keepalive連接池,無需TCP連接開銷。
keepalive_requests是一個設置,它調節客戶端可以通過單個keepalive連接進行的請求數量。 keepalive_timeout設置空閒keepalive連接保持打開的時間。
keepalive是一個與Nginx到上游服務器的連接相關的設置——當它充當代理或負載均衡器時。這意味著每個worker進程的空閒keepalive上游連接的數量。
啟用上游keepalive連接需要將這些指令放入Nginx主配置中:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
Nginx上游連接由ngx_http_upstream_module管理。
如果我們的前端應用程序不斷輪詢我們的後端應用程序以獲取更新,則增加keepalive_requests和keepalive_timeout將限制需要建立的連接數量。 keepalive指令不應過大,以允許其他連接到達我們的上游服務器。
這些設置的調整是根據具體情況進行的,需要進行測試。這也許是keepalive沒有默認設置的原因之一。
默認情況下,Nginx使用一個單獨的PHP進程來轉發PHP文件請求。在此,它充當代理(就像我們使用php7.0-fpm設置Apache時一樣)。
通常,我們使用Nginx的虛擬主機設置如下所示:
<code>locust --host=https://my-website.com </code>
由於FastCGI與HTTP是不同的協議,因此前兩行將一些參數和標頭轉發給php-fpm,而第三行指定了代理請求的方式——通過本地網絡套接字。
這對於多服務器設置來說是實用的,因為我們還可以指定要將請求代理到的遠程服務器。
但是,如果我們在單個系統上託管整個設置,則應使用Unix套接字連接到偵聽的php進程:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
Unix套接字被認為比TCP具有更好的性能,並且此設置被認為更安全。您可以在Rackspace的這篇文章中找到有關此設置的更多詳細信息。
關於Unix套接字的此技巧也適用於Apache。更多詳細信息在這裡。
gzip_static:關於Web服務器性能的普遍觀點是壓縮我們的靜態資源。這通常意味著我們將嘗試妥協,並嘗試僅壓縮超過某個閾值的文 件,因為在每次請求時動態壓縮資源可能代價高昂。 Nginx有一個gzip_static指令,允許我們提供文件的gzip版本(擴展名為.gz),而不是常規資源:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
這樣,Nginx將嘗試提供style.css.gz而不是style.css(在這種情況下,我們需要自己處理gzipping)。
這樣,CPU週期就不會浪費在每次請求的動態壓縮上。
關於Nginx的故事如果沒有提到如何緩存內容就不完整。 Nginx緩存非常高效,以至於許多系統管理員認為單獨的HTTP緩存層(如Varnish)沒有多大意義。 “簡單就是一種特性”。啟用Nginx緩存非常簡單。
<code>locust --host=https://my-website.com </code>
這是我們在虛擬主機文件中放置的指令,位於服務器塊之外。 proxy_cache_path參數可以是我們要存儲緩存的任何路徑。 levels指定Nginx應該在多少個目錄級別中存儲緩存的內容。出於性能原因,兩個級別通常是可以的。遍歷目錄可能會很費時。 keys_zone參數是用於存儲緩存鍵的共享內存區域的名稱,而10m是這些鍵在內存中的空間(10MB通常就足夠了;這不是實際緩存內容的空間)。 max_size是可選的,它設置緩存內容的上限——這裡是10GB。如果沒有指定,它將佔用所有可用空間。 inactive指定內容在未被請求之前可以在緩存中停留多長時間,然後被Nginx刪除。
設置好之後,我們將添加以下一行,其中包含內存區域的名稱到服務器或位置塊:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
通過告訴Nginx在遇到源服務器、上游服務器出錯或服務器宕機時從緩存中提供項目,可以實現Nginx的額外容錯層:
<code>sudo apt-get install libapache2-mod-fastcgi php7.0-fpm </code>
有關服務器或位置塊指令的更多詳細信息,以進一步調整Nginx緩存,請參見此處。
proxycache指令用於靜態資源,但我們通常希望緩存Web應用程序的動態輸出——無論是CMS還是其他內容。在這種情況下,我們將使用fastcgicache指令而不是proxycache*:
<code>sudo service start php7.0-fpm </code>
上面的最後一行將設置響應標頭以告知我們內容是從緩存中傳遞還是沒有。
然後,在我們的服務器或位置塊中,我們可以設置一些緩存例外——例如,當請求URL中存在查詢字符串時:
<code>sudo a2dismod php7.0 mpm_prefork sudo a2enmod mpm_event proxy_fcgi </code>
此外,在PHP的情況下,在服務器中的.php塊中,我們將添加類似以下內容:
<code><filesmatch> SetHandler "proxy:fcgi://127.0.0.1:9000/" </filesmatch></code>
在上面,fastcgi_cache*行和fastcgi_no_cache調節緩存和排除。所有這些指令的詳細參考可以在Nginx文檔網站上找到。
要了解更多信息,Nginx的工作人員為此主題提供了一個免費網絡研討會,並且有很多電子書可用。
我們試圖介紹一些有助於提高Web服務器性能的技術以及這些技術背後的理論。但這個主題絕非窮盡:我們仍然沒有涵蓋由Apache和Nginx組成的反向代理設置或多服務器設置。在這兩台服務器上取得最佳結果取決於測試和分析具體的實際案例。這是一個永無止境的話題。
Apache和Nginx都是功能強大的Web服務器,但在性能和優化能力方面存在顯著差異。 Apache是一個較舊的服務器,它使用進程驅動模型,為每個請求創建一個新線程。在處理多個並發連接時,這可能會導致大量的內存使用。另一方面,Nginx使用事件驅動架構,允許它同時處理數千個連接,而內存使用量極少。這使得Nginx更高效、更快,尤其是在靜態內容交付和反向代理場景中。
有多種方法可以優化Nginx以獲得更好的性能。首先,您可以調整worker進程和worker連接。 worker進程應設置為CPU或核心的數量,而worker連接應設置為最大打開文件限制。其次,您可以啟用gzip壓縮以減小Nginx發送給客戶端的數據大小。第三,您可以使用緩存將頻繁訪問的數據存儲在內存中,減少磁盤I/O操作。最後,您可以使用負載均衡將網絡流量分佈到多台服務器上,從而提高響應時間和整體性能。
可以通過多種方法優化Apache。首先,您可以調整MaxClients指令以控制最大並發連接數。其次,您可以啟用mod_deflate在將數據發送給客戶端之前對其進行壓縮,從而減少帶寬使用量。第三,您可以使用mod_cache進行緩存以將頻繁訪問的數據存儲在內存中,減少磁盤I/O操作。最後,您可以使用mod_proxy_balancer進行負載均衡以將網絡流量分佈到多台服務器上,從而提高響應時間和整體性能。
是的,您可以在反向代理設置中同時使用Apache和Nginx。在此配置中,Nginx充當處理客戶端請求的前端服務器,而Apache充當處理這些請求的後端服務器。此設置結合了兩個服務器的優勢,Nginx有效地處理靜態內容,而Apache提供動態內容處理。
由於其事件驅動架構,Nginx擅長提供靜態內容,這使其能夠同時處理數千個並發連接,而內存使用量極少。對於動態內容,Nginx可以將請求傳遞給應用程序服務器(如PHP-FPM)或將其代理到Apache服務器。但是,Nginx不像Apache使用其mod_php模塊那樣本地處理動態內容。
Apache可以提供靜態內容和動態內容。對於靜態內容,Apache使用其核心模塊。對於動態內容,Apache使用mod_php等附加模塊來處理PHP腳本。但是,Apache的進程驅動模型在處理多個並發連接時可能會導致大量的內存使用,這使其在靜態內容交付方面不如Nginx高效。
服務器優化可以顯著提高網站性能。它可以減少服務器響應時間,增加服務器可以處理的並發連接數,並減少帶寬使用量。這可以導致更快的頁面加載時間、更好的用戶體驗和改進的SEO排名。
在Apache和Nginx之間進行選擇取決於您的具體需求。如果您需要一個可以高效處理大量並發連接的服務器,或者您主要提供靜態內容,那麼Nginx可能是更好的選擇。如果您需要一個對動態內容處理具有強大支持的服務器,或者您依賴.htaccess文件進行配置,那麼Apache可能更合適。
Apache的常見性能問題包括在處理多個並發連接時內存使用量高和響應時間慢。對於Nginx,常見問題包括worker進程和連接的配置不當以及缺乏動態內容處理能力。
您可以使用各種工具監控Apache和Nginx的性能。對於Apache,您可以使用mod_status模塊來提供服務器狀態信息。對於Nginx,您可以使用stub_status模塊。此外,您可以使用New Relic、Datadog或Nagios等第三方監控工具來獲取更詳細的性能指標和警報。
以上是Apache vs nginx性能:優化技術的詳細內容。更多資訊請關注PHP中文網其他相關文章!