應用場景深度解析:Nignx效能優化指南
講師介
李強
天天拍車運維總監
- 網名:撒加,9年以上運維及管理經驗。先後在AdMaster、餓了麼擔任運維經理,現任天天拍車運維總監,主要負責天天拍車運維架構的管理、持續優化記運維團隊的建設、培養。
- 作為國內最早一批思科網路模擬器的推廣者、虛擬者先鋒論壇的創始人,一直致力於網路模擬器使用的推廣,為國內培養網路工程師盡一份力。
主題簡介:
- 基礎架構網路對Nignx效能的影響
- Nignx安裝優化
- Nignx配置檔案優化
- Nignx與HAProxy、Tomcat、PHP-FOM搭配使用時的注意事項
- TCP/IP協議棧優化對Nignx的影響
影響Nginx效能的因素
Nginx的優化不能單純看Nginx本身,其實有很多方面會影響到Nginx的整體效能。
1、網路層面
頻寬
頻寬對Nginx效能的影響是最為直接的,就算如何獨享10M的頻寬也肯定不如100M頻寬下Nginx的效能。另外,現在大部分公司的網站都擁有多個二級域名在提供服務,而這多個二級域名通常是共享一個出口頻寬的,這樣就造成Nginx來提供服務時,資源會受干擾。
網路質量
在中國的網際網路中,網路質量跟國外是沒法比的,經常出現的情況是客戶端到Nginx服務端總是會經過好幾個路由,但凡其中任何一個路由節點出現問題,會影響最終Nginx服務端的各種效能問題,例如資料重傳、資料超時等。
Nginx直連交換機收斂比
交換機收斂比通常指交換機接伺服器的下行流量跟交換機上行鏈路的比例,一般中型及中小型公司對IDC業務網路沒有過多的規劃,導致交換機收斂比通常是大於1的,也就意味著資料在交換機中是阻塞型轉發,會影響Nginx的資料轉發,尤其是作為反向代理時的影響更大。
Nginx架構中網路部署方式
大部分網際網路公司通常會採用如下結構的網路:
這種結構對比網際網路公司而言,請求需要經過路由器、防火牆、IDS/IPS等好幾層的裝置最終才能由業務伺服器來響應請求,這期間請求沒經過一層都需要消耗一定的時間,通過累計這個時間就會被放大,而且防火牆的效能直接決定了Nginx可以承載的請求數。所以迫不得已要用防火牆的話,請使用30萬以上的防火牆來做,幾萬的防火牆效能其實達不到業務需求的。
2、伺服器硬體層面
CPU
Nginx的工作模型是master-worker方式,簡單來說,越多的worker也就意味著越多的承載力,CPU的核心數從某種程度來說決定了Nginx最佳的worker工作數量。
記憶體
記憶體的容量直接決定了Nginx可承載的最大連線數。
硬碟
Nginx的使用過程中,容易產生IO的地方還是挺多的,比如各種臨時檔案、錯誤日誌、訪問日誌、快取等。而硬碟的傳輸速度可以影響到這些產生的IO,比如硬碟的轉速、硬碟的容量、硬碟的輸出頻寬等。
網絡卡
這裡以戴爾伺服器舉例,戴爾伺服器預設配置的網絡卡為Broadcom的,這種網絡卡一般使用沒問題,但遇上Nginx在處理大量小包的情況下,預設配置的網絡卡就會發生嚴重丟包的事情,核心層面的丟包會造成大量的資料重傳從而影響整個Nginx伺服器的效能。所以對於Nginx而言需要選擇合適的網絡卡。
3、作業系統層面
/etc/sysctl.conf的配置
很多時候大部分的運維都是從網上覆制貼上sysctl.conf的配置,對於其中一些value的配置都是沒有任何依據,所以容易被出現的各種TCP狀態而擔心。而這些抄來的配置也從根本上影響著Nginx的執行。
系統資源限制
這裡的資源限制主要指檔案控制代碼數的限制,檔案控制代碼數的多少限制了Nginx可以支援的最大連線數,不合理的配置會造成Nginx出現大量的500、502錯誤
IRQ Balanc
關於IRQ Balance服務,初衷是為了更好地利用CPU的資源來處理事務,但很多場景下,包括Nginx的應用,這個服務並不能起到利好的作用,反而會引起中斷的不平衡造成Nginx效能下降。
系統開放多餘的埠
嚴格來說,這種情況發生的機率還是比較低的。一般沒有做運維標準化的公司,在伺服器安裝系統時,預設就會開啟很多的服務,比如sendmail、postfix、ntpd、bind等等,這些服務一般監聽在所有的IP上,也就意味著Nginx部署在這種環境下,一旦有惡意攻擊者攻擊非Nginx的埠,就會造成整個伺服器資源被耗盡(DDoS的典型症狀),從而讓Nginx失去服務能力。
4、Nginx層面
編譯進業務不需要的模組
有很多運維對於Nginx的安裝大部分都是用yum或者apt-get進行安裝,這種安裝基本上都會把一些不需要的模組編譯進去,在Nginx提供高併發時多少還是要消耗一定的記憶體資源,前面我們說了記憶體直接決定了Nginx能夠承載多少連線的能力。
糟糕的配置
很多人發揮不了Nginx的效能,主要原因就是對Nginx的配置不熟悉,對於Nginx的引數指令不瞭解,所以各種各樣的配置五花八門,這裡實在沒法一一列舉,詳情可以參看度娘,一搜一大片。
快取使用不合理
Nginx的快取,本意是為了提升Nginx的處理能力,降低上游伺服器的壓力,而大部分運維也是從網上抄,於是帶來的結果就是快取的不合理配置造成響應延時過高、快取清理麻煩等問題。
5、上游伺服器層面
Nginx在大部分的應用場景要麼是通過http協議來反向代理上游伺服器,要麼是通過fastcgi協議來代理php應用,這兩類應用也是會對Nginx自身的效能帶來影響的。
PHP應用
當前一般都是使用PHP-FPM來提供fastcgi協議的接入,PHP的版本以及PHP-FPM的配置都會造成Nginx反代php-fpm時,容易發生502、504的錯誤。
Java應用
一般用於Java應用都逃不過Tomcat、jetty、resin,如果出現容器本身配置不合理、JVM不優化、Java容器選擇不合理,將直接影響Nginx做反代時的併發能力。
Nginx優化專案
Nginx的優化根據上述的效能影響因素來一一說明。
1、網路層面
- 在滿足業務以及價效比的前提下,越高的頻寬意味著Nginx可以承載更多的連線和併發;
- 在條件允許的情況下使用多個ISP運營商線路來提供服務,並使用策略路由解決多線路之間的請求響應;
- 在網路的部署架構中,儘量避免防火牆作為HTTP請求第一個經過的裝置,建議由Nginx或者第一級的4層負載均衡來轉發請求;
- 對於整個網站而言,有條件要設計大二層的網路(胖樹結構),這樣有利於伺服器的水平擴充套件;
- 對於使用IDC的使用者,建議選機房時使用mtr工具對目標機房進行72小的測試,來檢驗機房的網路質量,這裡主要包括請求到達機房時有沒有出現某一跳出現多個路由的情況、某一跳是否延遲超級不穩定(mtr工具中最後一列表示標準方差,簡單認為就是這個值越大這個節點越不穩定),通過這樣選出網路質量相對較好的機房;
- 在設計網路時,要考慮到交換機的收斂比,儘可能做到<=1,才能做到資料的線速轉發。
2、伺服器硬體層面
CPU
不管使用雲主機還是物理機,將Nginx獨立並且把Nginx作為負載均衡伺服器時,越多的CPU也就意味著Nginx可以啟用更多的worker來處理請求,這裡特別要說明下,CPU的主頻不需要作為參考指標,主要是物理CPU的核心數以及是否支援HT技術。
記憶體
越大的記憶體也就意味著Nginx理論上擁有的最大連線承載能力,而且Nginx使用到快取時,還可以將通過tmpfs將快取內容直接放在記憶體中,從而提高Nginx的處理效能;也可以使用tmpfs作為Nginx各種臨時檔案的存放地,降低磁碟的IO。
硬碟
用SSD、PCIe-SSD來替換SAS機械硬碟,在Nginx併發較高的伺服器上,可以將訪問日誌及錯誤日誌放在擁有更高IOPS的磁碟上,從而保證Nginx的效能,實際上,我們大部分時候測試Nginx時,是在測試Nginx伺服器上磁碟的效能。(大家好好回味下why)
網絡卡
上面網路層說要更高的頻寬,這個頻寬指運營商提供的服務頻寬,光有服務頻寬還不夠,還需要伺服器有對應的網絡卡來支援,比如用到了5G的頻寬,那伺服器網絡卡最少需要萬兆的,當然網絡卡的品牌也很重要。對於千兆的環境,建議使用英特爾i350的網絡卡,對於需要使用萬兆網絡卡的環境,建議優先選擇Mellanox的萬兆網絡卡(最便宜也要6000塊錢一塊,土豪用),資金有壓力的公司可以選擇英特爾x520的網絡卡,因為這些網絡卡除了小包的轉發效能強勁外,還有需要offload特性可以降低系統kernel的消耗,提升Nginx的承載能力。
只不過在用這些好的網絡卡時,一定要做中斷繫結(減少CPU0的中斷請求,讓充分發揮網路多佇列的優勢)。
3、作業系統層面
/etc/sysctl.conf
net.ipv4.tcp_max_tw_buckets = 1000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 8388608 16777216
net.ipv4.tcp_wmem = 4096 8388608 16777216
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_orphan_retries = 1 (TCP_FIN_WAIT1、TCP_FIN_WAIT2、TCP_LAST_ACK、TCP_CLOSING)
net.core.somaxconn = 262144
net.ipv4.tcp_max_orphans = 32768
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_retries2 = 5
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_slow_start_after_idle = 0
其中net.core.somaxconn這個配置在CentOS 6系列中,這個引數的值可以大於65535,在CentOS 7中,這個引數的最大值為65535
net.ipv4.tcp_timestamps
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_tw_reuse
這三個引數一般建議都配置為0,高併發時tw連線想快速回收及複用還是呵呵吧。
net.ipv4.tcp_slow_start_after_idle
這個引數是禁用TCP慢啟動的,當然如果使用CentOS 6的使用者建議將版本升級到6.5以及6.5以後的版本,這個引數配置才有意義,低於6.5的版本,需要做些特殊配置才可以。
其它注意事項
1、net.ipv4.tcp_mem沒事別瞎改,系統自動計算,別吃飽撐著!
2、fs.file-max沒事別瞎改,核心根據記憶體大小自動計算,表示kernel可分配的最大檔案控制代碼數,這個引數的大小簡單的來說約等於cat /proc/meminfo|grep MemTotal的大小乘以10%,精確的計算可以在我部落格檢視。
3、fs.nr_open可以修改,此為單個程序可分配的最大檔案控制代碼數,預設是為1024*1024
4、以下三個引數在Nginx沒有啟用SO_TCPKEEPALIVE特性時,根本不會生效,且啟用SO_TCPKEEPALIVE特性的引數,不是keepalive_timeout!
net.ipv4.tcp_keepalive_time
net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_intvl
HTTP Keep-Alive與TCP Keepalive是兩個不同的概念,簡單說前者是複用一個TCP連線,後者用於TCP連線的鏈路探活。
關於HTTP Keep-Alive與TCP Keepalive我部落格近期會發布一片文章,通過wireshark抓包來詳解。
5、net.ipv4.tcp_max_orphans不要設定過大,一個孤兒連線要佔用64K的不可交換記憶體,網上動輒這個值配置個幾百萬,吼吼,為記憶體感到擔憂。
6、net.ipv4.tcp_max_tw_buckets沒事別設定幾萬幾十萬的,這個引數是TW的連線超過這個設定時在/var/log/messages中列印警告資訊,同時釋放TW連線,可以設定小一點,當系統中TW太多時,net.ipv4.tcp_tw_recycle和net.ipv4.tcp_tw_reuse兩個引數並沒有卵用,所以TW連線過大的可以直接暴力的將net.ipv4.tcp_max_tw_buckets設定為0,並沒有什麼問題,當然最終解決還是要依賴程式和對Nginx的配置以及重新編譯核心,減少TW連線的產生。
7、rmem和wmem可根據netstat –s|grep socket的結果來判斷是否足夠,出現類似以下資訊時就要考慮增加對應的大小了。
269651 packets pruned from receive queue because of socket buffer overrun
5346082 packets collapsed in receive queue due to low socket buffer
Nginx層面
1、安裝優化
安裝優化主要針對Nginx的編譯安裝,這裡包含兩個部分,一是針對編譯器的編譯引數優化,二是通過Nginx的應用場景來定製化Nginx的編譯選項。以下通過Nginx最為靜態資源伺服器的案例來拋磚引玉地說下Nginx安裝的優化:
首先要確定Nginx是針對哪類靜態資源做服務,例如提供js、css以及jpg、png等資源的訪問,還是針對MP3、MP4、RMVB、zip、rar等大檔案的資源訪問。這兩種靜態資源的場景使用對Nginx的編譯引數還是有很大影響的。js\css\jpg等這類靜態資源一般檔案都是比較小的,作為網際網路公司一般檔案的平均大小不會超過4M,所以一般編譯時是不需要將–with-threads編譯進去,而後者這種檔案動輒都會超過10M的靜態資源,一般就需要將–with-threads開啟了,以便使用Nginx的執行緒池來提升檔案傳輸的效能。
其次,作為靜態資源伺服器,大部分的模組是使用不到的,能使用到的模組有ssl模組、http v2模組,所以編譯Nginx的引數就變成下面這樣了:
./configure –prefix=/opt/websuite/nginx \
–conf-path=/opt/config/nginx/nginx.conf \
–modules-path=/opt/websuite/nginx/modules \
–error-log-path=/opt/logs/nginx/error.log \
–http-log-path=/opt/logs/nginx/access.log \
–pid-path=/opt/run/nginx –user=websuite \
–group=websuite \
–with-file-aio \
–with-http_ssl_module \
–with-http_v2_module \
–with-http_stub_status_module \
–without-http_ssi_module \
–without-http_charset_module \
–without-http_access_module \
–without-http_auth_basic_module \
–without-http_autoindex_module \
–without-http_geo_module \
–without-http_split_clients_module \
–without-http_proxy_module \
–without-http_fastcgi_module \
–without-http_uwsgi_module \
–without-http_scgi_module \
–without-http_memcached_module \
–without-http_empty_gif_module \
–without-http_browser_module \
–without-http_upstream_hash_module \
–without-http_upstream_ip_hash_module \
–without-http_upstream_least_conn_module \
–without-http_upstream_keepalive_module \
–without-http_upstream_zone_module \
–http-client-body-temp-path=/opt/websuite/nginx/temp/client \
–without-mail_pop3_module \
–without-mail_imap_module \
–without-mail_smtp_module \
–with-google_perftools_module \
–with-pcre=/tmp/nginx/pcre-8.41 \
–with-pcre-jit \
–with-openssl=/tmp/nginx/openssl-1.0.2j \
–with-openssl-opt=”threads shared no-zlib no-comp no-ssl2 no-ssl3 no-ssl3-method”
通過以上的編譯引數編譯生成的Nginx可執行檔案是很小的,啟動時載入的動態連結庫也最少,在併發較高時,單個worker佔用的記憶體資源是很小的。
2、Nginx訪問日誌優化
訪問日誌的優化主要是3個方面:
- 增加buffer
- 使用map過濾不必要的日誌
- 制定合適的log_format
例如:
map $uri $expanded_name {
~^(.*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$) 0;
default 1;
}
access_log /opt/logs/openresty/access.www.grapenvine.cn proxy buffer=1m if=$expanded_name
表示使用proxy作為www.grapenvine.cn的日誌記錄格式,並且設定buffer為1m,當buffer中的日誌超過1m時再寫入日誌檔案,同時請求中包含map中指定的檔案將不記錄到訪問日誌裡。
Proxy相關引數優化
proxy_connect_timeout 30;
proxy_send_timeout 30;
proxy_read_timeout 60;
proxy_buffer_size 64k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_next_upstream invalid_header http_500 http_503 http_403 http_502 http_504;
proxy_next_upstream_timeout 1s;
proxy_next_upstream_tries 1;
其中proxy_buffer_size的值可通過統計一天accesslog中的$bytes_sent,$bytes_sent的平均值即為proxy_buffer_size的值,$bytes_sent的最大值則為proxy_buffers的大小。例如,平均值為50K,最大值為230k,則proxy_buffer_size就為64k,proxy_buffers就設定4 64k。
對於next_upstream的用法可參考 http://www.grapenvine.cn/post/95。
fastcgi模組的優化與proxy模組的優化方法類似。
3、合理利用tmpfs
通過使用tmpfs,可將Nginx的臨時目錄及快取目錄放到記憶體中,降低對磁碟IO的消耗。
4、Nginx主配置優化
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 100000;
pcre_jit on;
events{
use epoll;
worker_connections 8192;
accept_mutex off;
}
一般worker_connections* worker_processes < worker_rlimit_nofile,其中worker_rlimit_nofile不受ulimit –n的限制,如果沒有配置此引數,則以ulimit –n的值為主。
accept_mutex網站流量小設定為on也沒問題,大流量的情況下要設定為off,在nginx 1.11.3版本中預設就關閉了。
5、Nginx預設伺服器
建議配置一個default server,例如:
當你的Nginx要提供的服務很多時,增加default server中的backlog。加入reuseport可以讓所有的worker都監聽在80埠,提高Nginx的併發效能,reuseport與accept_mutex是互斥的。另外如果要啟用SO_TCPKEEPALIVE機制,需要在listen指令中配置so_keepalive=on
6、Nginx負載均衡
Nginx負載均衡環境中,在upstream啟用keepalive指令,可用在proxy和fastcgi的場景裡。
例如:
upstream netemu {
server unix:/opt/run/php/pool1.sock;
server unix:/opt/run/php/pool4.sock;
keepalive 4;
}
- 對於proxy模組需要增加指令
proxy_http_version 1.1;
proxy_set_header Connection “”;
另外上游伺服器必須配置keepalive timetout的相關配置,並且將這個超時時間可以設定長一點,比如10分鐘、半小時都可以,如果上游伺服器關閉了keepalive,那麼Nginx的配置指令裡配置了keepalive NUM,是沒什麼效果的。
- 對於fastcgi模組需要增加指令
fastcgi_keep_conn on;
- 當在upstream中使用負載均衡演算法時,演算法指令要放在keepalive指令前;
通過這種方式,可以顯著降低Nginx與upstream中server的timewait連線,keepalive的值不要設定過大,因為這是針對每個worker的。
Nginx優化的題外話
這個環節,主要是通過Nginx的第三方模組來擴充套件Nginx的功能。
- Nginx狀態輸出
nginx-module-vts
這個模組可以詳細地輸出Nginx的執行情況,通過json格式的輸出,還可以納入Zabbix監控中,來監控不同的vhost以及不同的upstream的執行狀態,便於詳細掌握Nginx的執行情況。
- 動態更新Nginx Upstream
ngx_dynamic_upstream
nginx-upsync-module
lua-upstream-nginx-module
動態更新nginx upstream其實是為了實現上游伺服器進行程式碼釋出時,讓上游伺服器能夠平滑的上下架,從而避免對使用者丟擲錯誤頁面的。其中nginx-upsync-module這個模組是最好用的,配合良好的程式碼釋出流程可以很好地實現上游伺服器的上下架。
- 緩解robots壓力
testcookie-nginx-module
相信很多做網際網路公司的運維童鞋,尤其是那些網際網路金融,還有提供各種優惠券的站點,最頭大的就是被別人惡意的刷短息介面、刷優惠碼等,這個模組對於那些沒有有Lua開發能力的公司而言,是個福音。
- 分散式共享快取
srcache-nginx-module+memc-nginx-module+memcached
srcache-nginx-module+memc-nginx-module+couchbase
srcache-nginx-module+redis2-nginx-module+redis
這三種方案都是用於將快取放入memcache或者Redis的,從而讓Nginx支援分散式快取,而且對於快取儲存不受Nginx本地儲存的影響,其中使用CouchBase的方案我正在測試中,有結果會發布在部落格上。
Q&A
Q1:Nginx可以快取動態內容嗎?
A1:可以,需要做一些條件判斷,簡單說就是把get方法的快取,post方法的bypass,後續大家可以參考我部落格裡的動態頁面快取。
Q2:新版本支援TCP嗎?
A2:支援,但不建議使用Nginx來做四層負載均衡。
Q3:如果對資料的實時性要求不高,能用Nginx把jsp頁面和後臺資料庫的互動資料快取?
A3:是的,而且可以對快取內容做控制,根據頁面時間的需要做配置。
Q4:我們業務有websocket?連線傳資料,這樣哪個更合適?
Q4:如果這樣,一般建議使用nginx的一個模組來實現,模組叫nchan來做websocket。
原文來自微信公眾號:DBAplus社群