秒懂!四步16點高效搞定高性能web服務器nginx
本文由馬哥教育面授班23期學員推薦,轉載自互聯網,作者為dongsong,內容略經小編改編和加工,觀點跟作者無關,最后感謝作者的辛苦貢獻與付出。
Nginx(讀音engine x)服務器由于性能優秀穩定、配置簡單以及跨平臺,被越來越多的公司和個人所采用,現已成為市場份額繼Apache之后的第二大Web服務器。各大小網站論壇博客也介紹說明了Nginx從安裝到優化的各種配置。
不過看了很多這些相關Nginx的文檔之后,發現一個比較大的問題,就是這些文檔基本也就從兩個方面著手,一是修改Nginx的配置文件,二是調整操作系統的相關內核參數;而且文檔說明也不夠明了,缺乏比較系統級別的優化。
本文將從Nginx源碼編譯安裝開始,到修改配置文件,調整系統內核參數以及架構四個方面著手分別介紹如何優化。
一.???? 安裝
(1) ?精簡模塊
Nginx由于不斷添加新的功能,附帶的模塊也越來越多。很多操作系統廠商為了用戶方便安裝管理,都增加了rpm、deb或者其他自有格式軟件包,可以本地甚至在線安裝。不過我不太建議使用這種安裝方式。這雖然簡化了安裝,在線安裝甚至可以自動解決軟件依賴關系,但是安裝后軟件的文件布局過于分散,不便管理維護;同時也正是由于存在軟件包之間的依賴關系,導致當有安全漏洞、或者其它問題,想要通過更新升級Nginx新版本時卻發現yum、deb源還未發布新版本(一般都落后于官網發布的軟件版本)。最重要的是采用非源碼編譯安裝的方式,默認會添加入許多模塊,比如郵件相關、uwsgi、memcache等等,很多網站運行時這些模塊根本未用到,雖然平時占用的資源很小,但是仍然可能是壓彎駱駝的一根稻草。各種非必需模塊默認安裝運行的同時,也給Web系統帶來了安全隱患。盡量保持軟件的輕裝上陣,是每個運維應當盡力做到的,所以我建議一般常用的服務器軟件使用源碼編譯安裝管理。。我一般使用的編譯參數如下,PHP相關模塊fastcgi被保留用作后文優化說明,:
./configure?\
"--prefix=/App/nginx"?\
"--with-http_stub_status_module"?\
"--without-http_auth_basic_module"?\
"--without-http_autoindex_module"?\
"--without-http_browser_module"?\
"--without-http_empty_gif_module"?\
"--without-http_geo_module"?\
"--without-http_limit_conn_module"?\
"--without-http_limit_req_module"?\
"--without-http_map_module"?\
"--without-http_memcached_module"?\
"--without-http_proxy_module"?\
"--without-http_referer_module"?\
"--without-http_scgi_module"?\
"--without-http_split_clients_module"?\
"--without-http_ssi_module"?\
"--without-http_upstream_ip_hash_module"?\
"--without-http_upstream_keepalive_module"?\
"--without-http_upstream_least_conn_module"?\
"--without-http_userid_module"?\
"--without-http_uwsgi_module"?\
"--without-mail_imap_module"?\
"--without-mail_pop3_module"?\
"--without-mail_smtp_module"?\
"--without-poll_module"?\
"--without-select_module"?\
"--with-cc-opt='-O2'"
編譯參數根據網站是否真正用到的原則增添或者減少,比如我們公司如果需要用到ssi模塊,從而能夠實現訪問shtml頁面,可以將第17行刪除,那么Nginx將默認安裝。大家可以通過運行 "./configure --help" 查看編譯幫助,決定是否需要安裝哪些模塊。
(2) ?GCC編譯參數優化 [可選項】
GCC總共提供了5級編譯優化級別:
-O0:無優化。
-O和-O1:使用能減少目標代碼尺寸以及執行時間并且不會使編譯時間明顯增加的優化。在編譯大型程序的時候會顯著增加編譯時內存的使用。
-O2:包含-O1的優化并增加了不需要在目標文件大小和執行速度上進行折衷的優化。編譯器不執行循環展開以及函數內聯。此選項將增加編譯時間和目標文件的執行性能。
-Os:可以看成 -O2.5,專門優化目標文件大小,執行所有的不增加目標文件大小的-O2優化選項,并且執行專門減小目標文件大小的優化選項。適用于磁盤空間緊張時使用。但有可能有未知的問題發生,況且目前硬盤容量很大,常用程序無必要使用。
-O3:打開所有 -O2 的優化選項外增加 -finline-functions、-funswitch-loops、-fgcse-after-reload 優化選項。相對于 -O2 性能并未有較多提高,編譯時間也最長,生成的目標文件也更大更占內存,有時性能不增反而降低,甚至產生不可預知的問題(包括錯誤),所以并不被大多數軟件安裝推薦,除非有絕對把握方可使用此優化級別。
修改GCC編譯參數,提高編譯優化級別,此方法適用于所有通過GCC編譯安裝的程序,不止Nginx。穩妥起見用 -O2,這也是大多數軟件編譯推薦的優化級別。查看Nginx源碼文件 auto/cc/gcc,搜索NGX_GCC_OPT,默認GCC編譯參數為-O,可以直接修改內容為NGX_GCC_OPT="-O2"或者在 ./configure配置時添加--with-cc-opt='-O2'選項。
二. ?????配置
應用服務器的性能優化主要在合理使用CPU、內存、磁盤IO和網絡IO四個方面,現在我們從Nginx配置文件 nginx.conf 入手進行優化:
(1) ?工作進程數的選擇
指令:worker_processes
定義了Nginx對外提供web服務時的工作進程數。最優值取決于許多因素,包括(但不限于)CPU核心的數量、存儲數據的硬盤數量及負載模式。不能確定的時候,將其設置為可用的CPU內核數將是一個好的開始(設置為“auto”將嘗試自動檢測它)。Shell執行命令??ps ax | grep "nginx: worker process" | grep -v "grep" 可以看到運行中的Nginx工作進程數,一般建議設置成服務器邏輯核心數,Shell執行命令?cat /proc/cpuinfo | grep processor | wc -l 可以檢測出服務器邏輯核心總數,偷懶可以直接寫auto,Nginx自適應。
(2) ?是否綁定CPU
指令:worker_cpu_affinity
綁定工作進程到對應CPU核心,Nginx默認未開啟CPU綁定。目前的服務器一般為多核CPU,當并發很大時,服務器各個CPU的使用率可能出現嚴重不均衡的局面,這時候可以考慮使用CPU綁定,以達到CPU使用率相對均勻的狀態,充分發揮多核CPU的優勢。top、htop等程序可以查看所有CPU核心的使用率狀況。綁定樣例:
worker_processes????4;
worker_cpu_affinity?0001?0010?0100?1000;
(3) ?打開文件數限制
指令:worker_rlimit_nofile
設定了每個Nginx工作進程打開的最大文件數,受限于系統的用戶進程打開文件數限制,未設置則使用系統默認值。理論上應該設置為當前Shell啟動進程的最大打開文件數除以Nginx的工作進程數。由于Nginx的工作進程打開文件數并不一完全均勻,所以可以將其設置成Shell啟動進程的最大打開文件數。Shell執行命令 ulimit -n?可以查看當前登錄Shell會話最大打開文件數數限制。Linux系統用戶進程默認同時打開文件最大數為1024,這個值太小,訪問量稍大就報“too many open files"。Shell執行命令先修改用戶打開文件數限制:
echo?"*?-?nofile?65536"?>>?/etc/security/limits.conf
然后添加入/etc/profile如下兩行內容,修改所有Shell和通過Shell啟動的進程打開文件數限制:
echo?"ulimit?-n?65536"?>>?/etc/profile
Shell執行命令使當前Shell臨時會話立即生效:
ulimit?-n?65536
(4) 驚群問題
指令:accept_mutex
如果?accept_mutex 指令值為 on 啟用,那么將輪流喚醒一個工作進程接收處理新的連接,其余工作進程繼續保持睡眠;如果值為 off 關閉,那么將喚醒所有工作進程,由系統通過use指令指定的網絡IO模型調度決定由哪個工作進程處理,未接收到連接請求的工作進程繼續保持睡眠,這就是所謂的“驚群問題”。Web服務器Apache的進程數很多,成百上千也是時有的事,“驚群問題”也尤為明顯。Nginx為了穩定,參數值保守的設置為 on 開啟狀態。可以將其設置成Off 提高性能和吞吐量,但這樣也會帶來上下文切換增多或者負載升高等等其它資源更多消耗的后果。
(5) ?網絡IO模型
指令:use
定義了Nginx設置用于復用客戶端線程的輪詢方法(也可稱多路復用網絡IO模型)。這自然是選擇效率更高的優先,Linux 2.6+內核推薦使用epoll,FreeBSD推薦使用kqueue,安裝時Nginx會自動選擇。
(6) ?連接數
指令:worker_connections
定義了Nginx一個工作進程的最大同時連接數,不僅限于客戶端連接,包括了和后端被代理服務器等其他的連接。官網文檔還指出了該參數值不能超過?worker_rlimit_nofile 值,所以建議設置成和?worker_rlimit_nofile 值相等。
(7) ?打開文件緩存
指令:open_file_cache
開啟關閉打開文件緩存,默認值 off 關閉,強烈建議開啟,可以避免重新打開同一文件帶來的系統開銷,節省響應時間。如需開啟必須后接參數 max=數字,設置緩存元素的最大數量。當緩存溢出時,使用LRU(最近最少使用)算法刪除緩存中的元素;可選參數 inactive=時間?設置超時,在這段時間內緩存元素如果沒有被訪問,將從緩存中刪除。示例:open_file_cache max=65536 ?inactive=60s。
指令:open_file_cache_valid
設置檢查open_file_cache緩存的元素的時間間隔。
指令:open_file_cache_min_uses
設置在由open_file_cache指令的inactive參數配置的超時時間內, 文件應該被訪問的最小次數。如果訪問次數大于等于此值,文件描述符會保留在緩存中,否則從緩存中刪除。
(8) ?日志相關
指令:access_log 和?error_log
當并發很大時,Nginx的訪問日志和錯誤日志的保存肯定會造成對磁盤的大量讀寫,也將影響Nginx的性能。并發量越大,IO越高。這時候可以考慮關閉訪問日志和錯誤日志,或者將日志保存到tmpfs文件系統里,或者減少保存的訪問日志條目和錯誤日志的級別,從而避免磁盤IO的影響。關閉日志使用 access_logoff。如必須保存日志,可以按每日或者每時或者其它時間段對日志做切割,這也可以減小IO,雖然可能效果不是特別大,不過因為日志文件尺寸變小了很多,也方便查閱或歸檔分析日志。一般線上環境建議錯誤日志設置為 error 或者 crit。自定義訪問日志的條目和錯誤日志的級別,詳細信息可以參閱官網或者網上其它文檔,按需修改。
(9) ?隱藏Nginx版本號
指令:server_tokens
開啟或關閉“Server”響應頭中輸出的Nginx版本號。推介設置為 off,關閉顯示響應頭的版本號,對性能的提高有小小的裨益,主要還是為了安全起見,不被駭客找到版本號對應的漏洞,從而被攻擊。
(10) 壓縮相關
指令:gzip
Nginx默認開啟了gzip壓縮功能。有可能很多人認為,開啟gzip壓縮會增加CPU的處理時間和負載。但是經過我們網站的測試發現,關閉了gzip壓縮功能的Nginx雖然減少了CPU計算,節省了服務器的響應時間,但網站頁面總體響應時間反而加長了,原因在于js和css、xml、json、html等等這些靜態文件的數據傳輸時間的增長大大超過了服務器節省出來的響應時間,得不償失。gzip on 開啟壓縮后,大約可以減少75%的文件尺寸,不但節省了比較多的帶寬流量,也提高了頁面的整體響應時間。所有建議還是開啟。當然也不是所有的靜態文件都需要壓縮,比如靜態圖片和PDF、視頻,文件本身就應當做壓縮處理后保存到服務器。這些文件再次使用gzip壓縮,壓縮的比例并不高,甚至適得其反,壓縮后文件尺寸增大了。CPU壓縮處理這些靜態文件增加占用的服務器響應時間絕大部分時候會超過了被壓縮減小的文件尺寸減少的數據傳輸時間,不劃算。是否需要對Web網站開啟壓縮,以及對哪些文件過濾壓縮,大家可以通過使用HttpWatch、Firebug等等網絡分析工具對比測試。
指令:gzip_comp_level
指定壓縮等級,其值從1到9,數字越大,壓縮率越高,越消耗CPU,負載也越高。9等級無疑壓縮率最高,壓縮后的文件尺寸也最小,但也是最耗CPU資源,負載最高,速度最慢的,這對于用戶訪問有時是無法忍受的。一般推薦使用1-4等級,比較折衷的方案。我們公司網站使用等級2。
指令:gzip_min_length
指定壓縮的文件最小尺寸,單位 bytes 字節,低于該值的不壓縮,超過該值的將被壓縮。我們網站設置為1k,太小的文件沒必要壓縮,壓縮過小尺寸文件帶來增加的CPU消耗時間和壓縮減少的文件尺寸降低的數據下載時間互相抵消,并有可能增加總體的響應時間。
指令:gzip_types
指定允許壓縮的文件類型,Nginx配置目錄 conf 下的?mime.types 文件存放了Nginx支持的文件類型,text/html類型文件,文件后綴為html htm shtml默認壓縮。推薦配置:gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript。
(11) 瀏覽器緩存
指令:expires
設置HTTP應答中的“Expires”和“Cache-Control”頭標。"Expires"一般結合"Last-Modified"使用。當設置了合理的expires配置時,瀏覽器第一次訪問Web頁面元素,會下載頁面中的的靜態文件到本機臨時緩存目錄下。第二次及之后再次訪問相同URL時將發送帶頭標識"If-Modified-Since"和本地緩存文件時間屬性值的請求給服務器,服務器比對服務器本地文件時間屬性值,如果未修改,服務器直接返回http 304狀態碼,瀏覽器直接調用本地已緩存的文件;如果時間屬性值修改了,重新發送新文件。這樣就避免了從服務器再次傳送文件內容,減小了服務器壓力,節省了帶寬,同時也提高了用戶訪問速度,一舉三得。指令后接數字加時間單位,即為緩存過期時間;-1 表示永遠過期,不緩存。強烈建議添加expires配置,過期時間的選擇具體分析。我們公司的部分Nginx配置如下:
location?~?.+\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires?30d;
}
location?~?.+\.(js|css|xml|javascript|txt|csv)$
{
expires?30d;
}
或者統一將靜態文件放在固定目錄下再對目錄做location和expires,示例:
location?/static/
{
expires?30d;
}
(12) 持久連接
指令:keepalive_timeout
啟用Http的持久連接Keepalive屬性,復用之前已建立的TCP連接接收請求、發送回應,減少重新建立TCP連接的資源時間開銷。在此的建議是當網站頁面內容以靜態為主時,開啟持久連接;若主要是動態網頁,且不能被轉化為靜態頁面,則關閉持久連接。后接數字和時間單位符號。正數為開啟持久連接,0關閉。
(13) 減少HTTP請求次數
網站頁面中存在大量的圖片、腳本、樣式表、Flash等靜態元素,減少訪問請求次數最大的優點就是減少用戶首次訪問頁面的加載時間。可以采用合并相同類型文件為一個文件的辦法減少請求次數。這其實屬于Web前端優化范疇,應當由Web前段工程師做好相關靜態文件的規劃管理,而不是由運維來做。不過Nginx也可以通過安裝阿里巴巴提供的Concat或者Google的PageSpeed模塊實現這個合并文件的功能。我們公司并未使用合并功能,具體安裝配置信息請查詢網上相關文檔,這里不再累述。Concat源代碼網址:https://github.com/alibaba/nginx-http-concat/,PageSpeed源代碼網址:https://github.com/pagespeed/ngx_pagespeed。
(14) PHP相關
Nginx不能直接解析PHP代碼文件,需要調用FastCGI接口轉給PHP解釋器執行,然后將結果返回給Nginx。PHP優化本文暫不介紹。Nginx可以開啟FastCGI的緩存功能,從而提高性能。
指令:fastcgi_temp_path
定義FastCGI緩存文件保存臨時路徑。
指令:fastcgi_cache_path
定義FastCGI緩存文件保存路徑和緩存的其它參數。緩存數據以二進制數據文件形式存儲,緩存文件名和key都是通過對訪問URL使用MD5計算獲得的結果。緩存文件先保存至fastcgi_temp_path指定的臨時目錄下,然后通過重命名操作移至fastcgi_cache_path指定的緩存目錄。levels指定了目錄結構,子目錄數以16為基數;keys_zone指定了共享內存區名和大小,用于保存緩存key和數據信息;inactive指定了緩存數據保存的時間,當這段時間內未被訪問,將被移出;max_size指定了緩存使用的最大磁盤空間,超過容量時將最近最少使用數據刪除。建議fastcgi_temp_path和fastcgi_cache_path設為同一分區,同分區移動操作效率更高。示例:
fastcgi_temp_path?/tmp/fastcgi_temp;
fastcgi_cache_path?/tmp/fastcgi_cache?levels=1:2?keys_zone=cache_fastcgi:16m?inactive=30m?max_size=1g;
示例中使用/tmp/fastcgi_temp作為FastCGI緩存的臨時目錄;/tmp/fastcgi_cache作為FastCGI緩存保存的最終目錄;一級子目錄為16的一次方16個,二級子目錄為16的2次方256個;共享內存區名為cache_fastcgi,占用內存128MB;緩存過期時間為30分鐘;緩存數據保存于磁盤的最大空間大小為1GB。
指令:fastcgi_cache_key
定義FastCGI緩存關鍵字。啟用FastCGI緩存必須加上這個配置,不然訪問所有PHP的請求都為訪問第一個PHP文件URL的結果。
指令:fastcgi_cache_valid
為指定的Http狀態碼指定緩存時間。
指令:fastcgi_cache_min_uses
指定經過多少次請求相同的URL將被緩存。
指令:fastcgi_cache_use_stale
指定當連接FastCGI服務器發生錯誤時,哪些情況使用過期數據回應。
指令:fastcgi_cache
緩存使用哪個共享內存區。
我常用nginx.conf模板,大家根據情況做適當修改:
[plain]view plaincopy
user??nginx?nginx;
worker_processes??auto;
error_log??logs/error.log?error;
pid????????logs/nginx.pid;
worker_rlimit_nofile????65536;
events
{
use?epoll;
accept_mutex?off;
worker_connections??65536;
}
http
{
include???????mime.types;
default_type??text/html;
charset?UTF-8;
server_names_hash_bucket_size???128;
client_header_buffer_size???????4k;
large_client_header_buffers??4?32k;
client_max_body_size????????????8m;
open_file_cache?max=65536??inactive=60s;
open_file_cache_valid??????80s;
open_file_cache_min_uses???1;
log_format??main??'$remote_addr?-?$remote_user?[$time_local]?"$request"?'
'$status?$body_bytes_sent?"$http_referer"?'
'"$http_user_agent"?"$http_x_forwarded_for"';
access_log??logs/access.log??main;
sendfile????on;
server_tokens?off;
fastcgi_temp_path??/tmp/fastcgi_temp;
fastcgi_cache_path?/tmp/fastcgi_cache?levels=1:2?keys_zone=cache_fastcgi:128m?inactive=30m?max_size=1g;
fastcgi_cache_key??$host$request_uri;
fastcgi_cache_valid?200?302?1h;
fastcgi_cache_valid?301?????1d;
fastcgi_cache_valid?any?????1m;
fastcgi_cache_min_uses?1;
fastcgi_cache_use_stale?error?timeout?http_500?http_503?invalid_header;
keepalive_timeout??60;
gzip??on;
gzip_min_length?1k;
gzip_buffers??4???64k;
gzip_http_version???1.1;
gzip_comp_level?2;
gzip_types?text/plain?text/css?application/json?application/x-javascript?text/xml?application/xml?application/xml+rss?text/javascript;
server
{
listen???????80;
server_name??localhost;
index????????index.html;
root?????????/App/web;
location?~?.+\.(php|php5)$
{
fastcgi_pass???unix:/tmp/php.sock;
fastcgi_index??index.php;
include????????fastcgi.conf;
fastcgi_cache??cache_fastcgi;
}
location?~?.+\.(gif|jpg|jpeg|png|bmp|swf|txt|csv|doc|docx|xls|xlsx|ppt|pptx|flv)$
{
expires?30d;
}
location?~?.+\.(js|css|html|xml)$
{
expires?30d;
}
location?/nginx-status
{
stub_status?on;
allow?192.168.1.0/24;
allow?127.0.0.1;
deny?all;
}
}
}
三.????????內核
Linux內核參數部分默認值不適合高并發,一般臨時方法可以通過調整/Proc文件系統,或者直接修改/etc/sysctl.conf配置文件永久保存。調整/Proc文件系統,系統重啟后還原至默認值,所以不推薦。Linux內核調優,主要涉及到網絡和文件系統、內存等的優化,下面是我常用的內核調優配置:
grep?-q?"net.ipv4.tcp_max_tw_buckets"?/etc/sysctl.conf?||?cat?>>?/etc/sysctl.conf?<<?EOF
########################################
net.core.rmem_default?=?262144
net.core.rmem_max?=?16777216
net.core.wmem_default?=?262144
net.core.wmem_max?=?16777216
net.core.somaxconn?=?262144
net.core.netdev_max_backlog?=?262144
net.ipv4.tcp_max_orphans?=?262144
net.ipv4.tcp_max_syn_backlog?=?262144
net.ipv4.tcp_max_tw_buckets?=?10000
net.ipv4.ip_local_port_range?=?1024?65500
net.ipv4.tcp_tw_recycle?=?1
net.ipv4.tcp_tw_reuse?=?1
net.ipv4.tcp_syncookies?=?1
net.ipv4.tcp_synack_retries?=?1
net.ipv4.tcp_syn_retries?=?1
net.ipv4.tcp_fin_timeout?=?30
net.ipv4.tcp_keepalive_time?=?600
net.ipv4.tcp_keepalive_intvl?=?30
net.ipv4.tcp_keepalive_probes?=?3
net.ipv4.tcp_mem?=?786432?1048576?1572864
fs.aio-max-nr?=?1048576
fs.file-max?=?6815744
kernel.sem?=?250?32000?100?128
vm.swappiness?=?10
EOF
sysctl?-p
四.架構
Nginx的最大優勢在于處理靜態文件和代理轉發功能,支持7層負載均衡和故障隔離。?動靜分離是每個網站發展到一定規模之后必然的結果。靜態請求則應當最好將其拆分,并啟用獨立的域名,既便于管理的需要,也便于今后能夠快速支持CDN。如果一臺Nginx性能無法滿足,則可以考慮在Nginx前端添加LVS負載均衡,或者F5等硬件負載均衡(費用昂貴,適合土豪公司單位),由多臺Nginx共同分擔網站請求。還可以考慮結合Varnish或者Squid緩存靜態文件實現類似CDN功能。
新版Nginx目前已經支持直接讀寫Memcache,可以編譯安裝時候選擇添加此類模塊,從而節省了轉交給PHP或者JPS等動態程序服務器處理時間,提高效率的同時,減小了動態服務器的負載。