Nginx高并發下的優化
寫在前面
最近在進行服務器的優化,正好在看nginx相關的知識,所以把一些知識整理一下。參考資料為《Nginx高性能web服務器詳解》,建議大家都去讀讀這本書。 我的機器為四核CPU,16G內存。
內核參數優化
把如下的參數追加到Linux系統的/etc/sysctl.conf
文件中,然后使用如下命令使修
改生效:/sbin/sysctl -p
net.core.somaxconn = 262144
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1</pre>
net.core.netdev_max_backlog參數
參數net.core.netdev_max_backlog,表示當每個網絡接口接受數據包的速率比內核處理這些包的速率快時,允許發送隊列的數據包的最大數目,我們調整為262144.
net.core.somaxconn
該參數用于調節系統同時發起的TCP連接數,一般默認值為128,在客戶端高并發的請求的情況下,該默認值較小,可能導致連接超時或者重傳問題,我們可以根據實際情況結合并發數來調節此值。
net.ipv4.tcp_max_orphans
該參數用于設定系統中最多允許存在多少TCP套接字不被關聯到任何一個用戶文件句柄上,如果超過這個數字,沒有與用戶文件句柄關聯到TCP套接字將立即被復位,同時發出警告信息,這個限制只是為了簡單防治Dos攻擊,一般系統內存充足的情況下,可以增大這個參數。
net.ipv4.tcp_max_syn_backlog
該參數用于記錄尚未收到客戶端確認信息的連接請求的最大值,對于擁有128內存的系統而言,此參數的默認值為1024,對小內存的系統則是128,一般在系統內存比較充足的情況下,可以增大這個參數的賦值
net.ipv4.tcp_timestamps
該參數用于設置時間戳,這個可以避免序列號的卷繞,在一個1Gb/s的鏈路上,遇到以前用過的序列號概率很大,當此值賦值為0時,警用對于TCP時間戳的支持,默認情況下,TCP協議會讓內核接受這種異常的數據包,針對Nginx服務器來說,建議將其關閉。
net.ipv4.tcp_synack_retries
該參數用于設置內核放棄TCP連接之前向客戶端發送SYN+ACK包的數量,為了建立對端的連接服務,服務器和客戶端需要進行三次握手,第二次握手期間,內核需要發送SYN并附帶一個回應前一個SYN的ACK,這個參數主要影響這個過程,一般賦予值為1,即內核放棄連接之前發送一次SYN+ACK包。
net.ipv4.tcp_syn_retries
該參數的作用與上一個參數類似,設置內核放棄建立連接之前發送SYN包的數量,賦值為1。
nginx優化
nginx主的配置文件如下:
user nginx;
worker_processes 2;
worker_cpu_affinity 0001 0010;
worker_rlimit_nofile 1024;
?
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
?
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
?
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
?
log_format main 'remote_user [
request" '
'body_bytes_sent "
http_user_agent" "$http_x_forwarded_for"';
?
access_log /var/log/nginx/access.log main;
?
sendfile on;
tcp_nopush on;
tcp_nodelay on;
?
keepalive_timeout 65;
?
reset_timedout_connection on;
send_timeout 10;
types_hash_max_size 1024;
client_header_buffer_size 4k;
client_max_body_size 8m;
?
gzip on;
gzip_disable "msie6"
gzip_min_length 1k;
gzip_comp_level 4;
gzip_vary on;
gzip_static on;
gzip_buffers 4 32k;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_http_version 1.1;
?
include /etc/nginx/conf.d/*.conf;
}</pre>
worker_processes (cpu內核)
worker_processes用來設置Nginx服務的進程數。推薦是CPU內核數或者內核數的倍數,推薦使用CPU內核數,因為我的CPU為4核的,所以設置為4。
worker_cpu_affinity (cpu優化)
默認情況下,Nginx的多個進程有可能跑在某一個CPU或CPU的某一核上,導致Nginx進程使用硬件的資源不均,因此綁定Nginx進程到不同的CPU上是為了充分利用硬件的多CPU多核資源的目的。
worker_cpu_affinity用來為每個進程分配CPU的工作內核,參數有多個二進制值表示,每一組代表一個進程,每組中的每一位代表該進程使用CPU的情況,1代表使用,0代表不使用。所以我們使用worker_cpu_affinity 0001 0010 0100 1000;
來讓進程分別綁定不同的核上。
worker_processes 4;
$nginx 進程數,建議按照cpu 數目來指定,一般為它的倍數 (如,2個四核的cpu計為8)。
?
比如4核配置:
worker_cpu_affinity 0001 0010 0100 1000
?
比如8核配置:
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
?
為每個進程分配cpu,上例中將8 個進程分配到8 個cpu,當然可以寫多個,或者將一個進程分配到多個cpu。
邏輯CPU個數:
cat /proc/cpuinfo | grep "processor" | wc -l
?
物理CPU個數:
cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l
?
每個物理CPU中Core的個數:
cat /proc/cpuinfo | grep "cpu cores" | wc -l
worker_rlimit_nofile(最大打開文件數)
進程的最大打開文件數限制。這樣nginx就不會有“too many open files”問題了。
該指令是當一個Nginx進程打開的最多文件描述符數目,理論值應該是最多打開文件數(ulimit-n)與nginx進程數相除,但是Nginx分配請求并不是那么均勻,所以最好與ulimit -n 的值保持一致
$ worker_rlimit_nofile 1024; #最好與ulimit -n保持一致
?
當nginx充當反向代理服務器時max_client的計算
$ max_client = worker_processes * worker_connections / 4
max_client表示的是nginx充當反向代理服務器時可同時承載的最大連接數,但是為什么需要處理4呢?這是因為在反向代理時,瀏覽器會和nginx建立2條連接,nginx也會建立對應的2條連接到后端服務器,因此就有4條連接了,所以需要除以4.</pre>
worker_connections(worker最大連接數)
設置一個進程理論允許的最大連接數,理論上越大越好,但不可以超過worker_rlimit_nofile的值。還有個問題,linux系統中有個指令open file resource limit,它設置了進程可以打開的文件句柄數量,可以用下面的指令查看你的linux系統中open file resource limit指令的值,`
echo "2390251" > /proc/sys/fs/file-max; sysctl -p
?
worker_connections:表示nginx的工作進程(worker process)可以允許建立的外部連接數。指的是單個worker對并發的最大連接數。
?
在Nginx運行的時候,會啟動兩種進程,一種是主進程master process;一種是工作進程worker process。如果我在配置文件中將worker_processes設置為4,啟動Nginx后,使用進程查看命令觀察名字叫做nginx的進程信息,我會看到如下結果:
一個nginx主進程(master process);
四個工作進程(worker process)。
主進程負責監控端口,協調worker進程的工作狀態,分配工作任務,worker進程負責進行任務處理。一般這個參數要和操作系統的CPU內核數成倍數。
?
注意:
worker_connections:這個屬性是指單個工作進程可以允許同時建立外部連接的數量。無論這個連接是外部主動建立的,還是內部建立的。一個工作進程(worker process)建立一個連接后,進程將會打開一個文件副本,所以這個數(worker_connections)的大小還和操作系統設定的進程最大可打開的文件副本數有關。
進程的最大連接數受 Linux 系統進程的最大打開文件數限制,只有執行了 "ulimit -HSn 65535" 之后,worker_connections 才能生效
events {
單個進程允許的客戶端最大連接數
worker_connections 65535; #添加的
}</pre>
use epoll(epoll模型)
設置事件驅動模型使用epoll。事件驅動模型有select、poll等。
- select先創建事件的描述符集合,對于一個描述符,可以關注其上面的Read事件、Write事件以及Exception事件,所以要創建三類事件描述符集合,分別用來處理Read事件的描述符、Write事件的描述符、Exception事件的描述符,然后調用底層的select()函數,等待事件發生,輪詢所有事件描述符集合的每一個事件描述符,檢查是否有事件發生,有的話就處理。select效率低,主要是輪詢效率低,而且還要分別輪詢三個事件描述符的集合。
- poll方法與select類似,都是先創建一個關注事件的描述符集合,再去等待這些事件發生,然后再輪詢描述符集合,檢查有無事件發生,如果有,就去處理。不同點是poll為Read事件、Write事件以及Exception事件只創建一個集合,在每個描述符對應的結構上分別設置Read事件、Write事件以及Exception事件。最后輪詢的時候,可以同時檢察權這三個事件是否發生??梢哉f,poll庫是select庫的優化實現。
- epoll是Nginx支持的高性能事件驅動庫之一。是公認的非常優秀的事件驅動模型。和poll庫跟select庫有很大的不同,最大區別在于效率。我們知道poll庫跟select庫都是創建一個待處理的事件列表,然后把這個列表發給內核,返回的時候,再去輪詢檢查這個列表,以判斷事件是否發生。這樣在描述符多的應用中,效率就顯得比較低下了。一種比較好的方式是把列表的管理交由內核負責,一旦某種事件發生,內核就把發生事件的描述符列表通知給進程,這樣就避免了輪詢整個描述符列表。首先,epoll庫通過相關調用同志內核創建一個有N個描述符的事件列表,然后給這些描述符設置所關注的事件,并把它添加到內核的事件列表中去。完成設置以后,epoll庫就開始等待內核通知事件發生了,某一事件發生后,內核講發生事件的描述符列表上報給epoll庫,得到事件列表的epoll庫,就可以進行事件處理了。epoll庫在linux平臺是高效的,它支持一個進程打開大數目的事件描述符,上限是系統可以打開文件的最大數目;同時,epoll庫的IO效率不隨描述符數量的增加而線性下降,因為它只會對內核上報的活躍的描述符進行操作。
$ use epoll #打開epoll,默認是關閉的 </pre>
mutex_accept
multi_accept告訴nginx收到一個新連接通知后,盡可能接受鏈接。當然,得讓他開著。
$ multi_accept on;</pre>
sendfile(傳輸文件)
使用開啟或關閉是否使用sendfile()傳輸文件,普通應用應該設為on,下載等IO重負荷的應用應該設為off,因為大文件不適合放到buffer中。
傳統文件傳輸中(read/write方式)在實現上3其實是比較復雜的,需要經過多次上下文切換,當需要對一個文件傳輸時,傳統方式是:
- 調用read函數,文件數據被copy到內核緩沖區
- read函數返回,數據從內核緩沖區copy到用戶緩沖區
- write函數調用,將文件數據從用戶緩沖區copy到內核與socket相關的緩沖區
- 數據從socket緩沖區copy到相關協議引擎
從上面可以看出來,傳統readwrite進行網絡文件傳輸的方式,在過程中經歷了四次copy操作。
硬盤->內核buffer->用戶buffer->socket相關緩沖區->協議引擎
而sendfile系統調用則提供了一種減少多次copy,提高文件傳輸性能的方法。流程如下: - sendfile系統效用,文件數據被copy至內核緩沖區
- 記錄數據文職和長度相關的數據保存到socket相關緩存區
- 實際數據由DMA模塊直接發送到協議引擎
$ sendfile on; #默認打開
tcp_nopush
sendfile為on時這里也應該設為on,數據包會累積一下再一起傳輸,可以提高一些傳輸效率。
$ tcp_nopush on;默認打開
</pre>
tcp_nodelay
看上去是和tcp_nopush相反的功能,但是兩邊都為on時nginx也可以平衡這兩個功能的使用這個能夠提高高頻發送小數據報文的實時性。系統存在高頻發送小數據報文的時候,打開它。
$ tcp_nodelay on; #小的數據包不等待直接傳輸。默認為on。
keepalive_timeout
配置連接 keep-alive 超時時間,服務器將在超時之后關閉相應的連接。指定了與客戶端的 keep-alive 鏈接的超時時間。服務器會在這個時間后關閉鏈接。keep-alive設置過小客戶端和服務器會頻繁建立連接;設置過大由于連接需要等待keep-alive才會關閉,所以會造成不必要的資源浪費。
$ keepalive_timeout 65; #默認65
reset_timedout_connection
允許server在client停止響應以后關閉連接,釋放分配給該連接的內存。當有大并發需求時,建議打開。
$ reset_timedout_connection off; #默認off
send_timeout
設置Nginx服務器響應客戶端的超時時間,這個超時時間只針對兩個客戶端和服務器建立連接后,某次活動之間的時間,如果這個時間后,客戶端沒有任何活動,Nginx服務器將關閉連接,將其設置為10s,Nginx與客戶端建立連接后,某次會話中服務器等待客戶端響應超過10s,就會自動關閉。
$ send_timeout 10;
types_hash_max_size
types_hash_max_size影響散列表的沖突率。types_hash_max_size越大,就會消耗更多的內存,但散列key的沖突率會降低,檢索速度就更快。types_hash_max_size越小,消耗的內存就越小,但散列key的沖突率可能上升。
$types_hash_max_size 1024; #默認1024
client_header_buffer_size
該指令用于設置Nginx服務器允許的客戶端請求頭部的緩沖區大小,默認為1KB,此指令的賦值可以根據系統分頁大小來設置,分頁大小可以用以下命令獲取getconf PAGESIZE
。
$ client_header_buffer_size 4k;
client_max_body_size
客戶端上傳的body的最大值。超過最大值就會發生413(Request Entity Too Large)錯誤。默認為1m,最好根據自己的情況改大一點。
$ client_max_body_size 8m;
limit_conn
limit_conn_zone設置用于保存各種key(比如當前連接數)的共享內存的參數.5m就是5兆字節,這個值應該被設置的 足夠大以存儲(32K5)32byte狀態或者(16K5)64byte狀態
limit_conn addr為給定的key設置最大連接數.這里key是addr,我們設置的值是100,也就是說我們允許每一個IP地 址最多同時打開有100個連接
binary_remote_addr zone=addr:5m;
$ limit_conn addr 100;
proxy(后端服務器超時時間是否合理)
proxy_connect_timeout :后端服務器連接的超時時間_發起握手等候響應超時時間
proxy_read_timeout:連接成功后等候后端服務器響應時間其實已經進入后端的排隊之中等候處理(也可以說是后端 服務器處理請求的時間)
proxy_send_timeout :后端服務器數據回傳時間_就是在規定時間之內后端服務器必須傳完所有的數據
proxy_send_timeout #默認60
$ proxy_connect_timeout #默認60
open_file_cache
句法: open_file_cache off;
open_file_cache max=N [inactive=time];
默認: open_file_cache off;
語境: http,server,location
open_file_cache打開緩存的同時也指定了緩存最大數目,以及緩存的時間我指定了20s,所以等到至少20s不訪問這個文件,相應緩存的這個文件的更改信息才會被刪除。
$ open_file_cache max=100000 inactive=20s;
open_file_cache_valid在open_file_cache指多長時間檢查一次緩存的有效信息。也就是說即使我一直訪問這個文件,30s后會檢查此文件的更改信息是否變化,發現變化就更新
$ open_file_cache_valid 30s;
open_file_cache_min_uses 定義了open_file_cache中指令參數不活動時間期間里最小的文件數,如果超過這個數字,文件更改信息一直是在緩存中打開的
$ open_file_cache_min_uses 2;
open_file_cache_errors指定了當搜索一個文件時是否緩存錯誤信息,
$ open_file_cache_errors off;
gzip on
啟用gzip,對響應數據進行在線實時壓縮,減少數據傳輸量。
$ gzip on
gzip_disable
Nginx服務器在響應這些種類的客戶端請求時,不使用Gzip功能緩存應用數據,gzip_disable "msie6"對IE6瀏覽器的數據不進行GZIP壓縮。
$ gzip_disable "msie6";
gzip_min_length
Gzip壓縮功能對大數據的壓縮效果明顯,但是如果壓縮很小的數據,可能出現越壓縮數據量越大的情況,因此應該根據相應頁面的大小,選擇性開啟或者關閉Gzip功能。建議將值設置為1KB。
警告:如果一個請求小于1K,我們最好不要壓縮它,因為壓縮這些小的數據會降低處理此請求的所有進程的速度
$ gzip_min_length 1k; #最小1K的文件才啟動壓縮
gzip_comp_level
設置壓縮程度,包括級別1到級別9,級別1表示壓縮程度最低,壓縮效率最高;級別9表示壓縮程度最高,壓縮效率最低,最費時間。
$ gzip_comp_level 9; #壓縮的級別
gzip_vary
用于設置在使用Gzip功能時是否發送帶有“Vary:Accept-Encoding”頭域的響應頭部,該頭域的主要功能是告訴接收方發送的數據經過了壓縮處理,開啟后端效果是在響應頭部Accept-Encoding: gzip,對于本身不支持Gzip的壓縮的客戶端瀏覽器是有用的。
$ gzip_vary on; #讓前邊的緩存服務器識別壓縮后的文件
gzip_buffers;
該指令用于設置Gzip壓縮文件使用存儲空間的大小,
語法: number: 指定Nginx服務器需要向系統申請存儲空間的個數,
gzip_buffers 4 32k; #壓縮過程都寫到buffer里面,壓縮完成才發給客戶端
gzip_static
開啟時,如果客戶端瀏覽器不支持Gzip處理,Nginx服務器將返回解壓后的數據,如果客戶端瀏覽器支持Gzip處理,Nginx服務器忽略該指令設置。仍然返回壓縮數據。
$ gunzip_static on; #開啟
gzip_types
Nginx服務器可以根據MIME類型選擇性開啟Gzip壓縮功能。該指令涌來設置MIME類型。設置需要壓縮的數據格式
$ gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_http_version
gzip_http_version 默認值: gzip_http_version 1.1(就是說對HTTP/1.1協議的請求才會進行gzip壓縮)
注:99.99%的瀏覽器基本上都支持gzip解壓了。但是假設我們使用的是默認值1.1,如果我們使用了proxy_pass進行反向代理,那么nginx和后端的upstream server之間是用HTTP/1.0協議通信的,如果我們使用nginx通過反向代理做Cache Server,而且前端的nginx沒有開啟gzip,同時,我們后端的nginx上沒有設置gzip_http_version為1.0,那么Cache的url將不會進行gzip壓縮
$ gzip_http_version 1.1 #gzip版本
查考鏈接
https://www.cnblogs.com/yuanzai12345/p/5951860.html