寫在前面
最近在進行服務器的優(yōu)化,正好在看nginx相關的知識,所以把一些知識整理一下。參考資料為《Nginx高性能web服務器詳解》,建議大家都去讀讀這本書。
我的機器為四核CPU,16G內(nèi)存。
內(nèi)核參數(shù)優(yōu)化
把如下的參數(shù)追加到Linux系統(tǒng)的/etc/sysctl.conf文件中,然后使用如下命令使修改生效:/sbin/sysctl -p
net.core.netdev_max_backlog = 262144
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
net.core.netdev_max_backlog參數(shù)
參數(shù)net.core.netdev_max_backlog,表示當每個網(wǎng)絡接口接受數(shù)據(jù)包的速率比內(nèi)核處理這些包的速率快時,允許發(fā)送隊列的數(shù)據(jù)包的最大數(shù)目,我們調(diào)整為262144.
net.core.somaxconn
該參數(shù)用于調(diào)節(jié)系統(tǒng)同時發(fā)起的TCP連接數(shù),一般默認值為128,在客戶端高并發(fā)的請求的情況下,該默認值較小,可能導致連接超時或者重傳問題,我們可以根據(jù)實際情況結合并發(fā)數(shù)來調(diào)節(jié)此值。
net.ipv4.tcp_max_orphans
該參數(shù)用于設定系統(tǒng)中最多允許存在多少TCP套接字不被關聯(lián)到任何一個用戶文件句柄上,如果超過這個數(shù)字,沒有與用戶文件句柄關聯(lián)到TCP套接字將立即被復位,同時發(fā)出警告信息,這個限制只是為了簡單防治Dos攻擊,一般系統(tǒng)內(nèi)存充足的情況下,可以增大這個參數(shù)。
net.ipv4.tcp_max_syn_backlog
該參數(shù)用于記錄尚未收到客戶端確認信息的連接請求的最大值,對于擁有128內(nèi)存的系統(tǒng)而言,此參數(shù)的默認值為1024,對小內(nèi)存的系統(tǒng)則是128,一般在系統(tǒng)內(nèi)存比較充足的情況下,可以增大這個參數(shù)的賦值。
net.ipv4.tcp_timestamps
該參數(shù)用于設置時間戳,這個可以避免序列號的卷繞,在一個1Gb/s的鏈路上,遇到以前用過的序列號概率很大,當此值賦值為0時,警用對于TCP時間戳的支持,默認情況下,TCP協(xié)議會讓內(nèi)核接受這種異常的數(shù)據(jù)包,針對Nginx服務器來說,建議將其關閉。
net.ipv4.tcp_synack_retries
該參數(shù)用于設置內(nèi)核放棄TCP連接之前向客戶端發(fā)送SYN+ACK包的數(shù)量,為了建立對端的連接服務,服務器和客戶端需要進行三次握手,第二次握手期間,內(nèi)核需要發(fā)送SYN并附帶一個回應前一個SYN的ACK,這個參數(shù)主要影響這個過程,一般賦予值為1,即內(nèi)核放棄連接之前發(fā)送一次SYN+ACK包。
net.ipv4.tcp_syn_retries
該參數(shù)的作用與上一個參數(shù)類似,設置內(nèi)核放棄建立連接之前發(fā)送SYN包的數(shù)量,賦值為1。
nginx優(yōu)化
nginx的配置文件如下:
user www-data;
pid /run/nginx.pid;
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
worker_rlimit_nofile 65535;
events {
use epoll;
worker_connections 65535;
accept_mutex off;
multi_accept off;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 60 50;
send_timeout 10s;
types_hash_max_size 2048;
client_header_buffer_size 4k;
client_max_body_size 8m;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_min_length 1024;
gzip_vary on;
gzip_comp_level 2;
gzip_buffers 32 4k;
gunzip_static on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
worker_processes
worker_processes用來設置Nginx服務的進程數(shù)。推薦是CPU內(nèi)核數(shù)或者內(nèi)核數(shù)的倍數(shù),推薦使用CPU內(nèi)核數(shù),因為我的CPU為4核的,所以設置為4。
worker_cpu_affinity
默認情況下,Nginx的多個進程有可能跑在某一個CPU或CPU的某一核上,導致Nginx進程使用硬件的資源不均,因此綁定Nginx進程到不同的CPU上是為了充分利用硬件的多CPU多核資源的目的。
worker_cpu_affinity用來為每個進程分配CPU的工作內(nèi)核,參數(shù)有多個二進制值表示,每一組代表一個進程,每組中的每一位代表該進程使用CPU的情況,1代表使用,0代表不使用。所以我們使用worker_cpu_affinity 0001 0010 0100 1000;來讓進程分別綁定不同的核上。
worker_connections
設置一個進程理論允許的最大連接數(shù),理論上越大越好,但不可以超過worker_rlimit_nofile的值。還有個問題,linux系統(tǒng)中有個指令open file resource limit,它設置了進程可以打開的文件句柄數(shù)量,可以用下面的指令查看你的linux系統(tǒng)中open file resource limit指令的值,cat /proc/sys/fs/file-max
可以將該指令設置為23900251
echo "2390251" > /proc/sys/fs/file-max; sysctl -p
worker_rlimit_nofile
設置毎個進程的最大文件打開數(shù)。如果不設的話上限就是系統(tǒng)的ulimit –n的數(shù)字,一般為65535。
use epoll
設置事件驅(qū)動模型使用epoll。事件驅(qū)動模型有select、poll、poll等。
select先創(chuàng)建事件的描述符集合,對于一個描述符,可以關注其上面的Read事件、Write事件以及Exception事件,所以要創(chuàng)建三類事件描述符集合,分別用來處理Read事件的描述符、Write事件的描述符、Exception事件的描述符,然后調(diào)用底層的select()函數(shù),等待事件發(fā)生,輪詢所有事件描述符集合的每一個事件描述符,檢查是否有事件發(fā)生,有的話就處理。select效率低,主要是輪詢效率低,而且還要分別輪詢?nèi)齻€事件描述符的集合。
poll方法與select類似,都是先創(chuàng)建一個關注事件的描述符集合,再去等待這些事件發(fā)生,然后再輪詢描述符集合,檢查有無事件發(fā)生,如果有,就去處理。不同點是poll為Read事件、Write事件以及Exception事件只創(chuàng)建一個集合,在每個描述符對應的結構上分別設置Read事件、Write事件以及Exception事件。最后輪詢的時候,可以同時檢察權這三個事件是否發(fā)生。可以說,poll庫是select庫的優(yōu)化實現(xiàn)。
epoll是Nginx支持的高性能事件驅(qū)動庫之一。是公認的非常優(yōu)秀的事件驅(qū)動模型。和poll庫跟select庫有很大的不同,最大區(qū)別在于效率。我們知道poll庫跟select庫都是創(chuàng)建一個待處理的事件列表,然后把這個列表發(fā)給內(nèi)核,返回的時候,再去輪詢檢查這個列表,以判斷事件是否發(fā)生。這樣在描述符多的應用中,效率就顯得比較低下了。一種比較好的方式是把列表的管理交由內(nèi)核負責,一旦某種事件發(fā)生,內(nèi)核就把發(fā)生事件的描述符列表通知給進程,這樣就避免了輪詢整個描述符列表。首先,epoll庫通過相關調(diào)用同志內(nèi)核創(chuàng)建一個有N個描述符的事件列表,然后給這些描述符設置所關注的事件,并把它添加到內(nèi)核的事件列表中去。完成設置以后,epoll庫就開始等待內(nèi)核通知事件發(fā)生了,某一事件發(fā)生后,內(nèi)核講發(fā)生事件的描述符列表上報給epoll庫,得到事件列表的epoll庫,就可以進行事件處理了。epoll庫在linux平臺是高效的,它支持一個進程打開大數(shù)目的事件描述符,上限是系統(tǒng)可以打開文件的最大數(shù)目;同時,epoll庫的IO效率不隨描述符數(shù)量的增加而線性下降,因為它只會對內(nèi)核上報的活躍的描述符進行操作。
accept_mutex
這個牽扯到《UNIX網(wǎng)絡編程》第一卷中提到的“驚群”問題(Thundering herd problem),大致意思是當某一時刻只有一個網(wǎng)絡連接到來時,多個睡眠進程會被同時叫醒,但只有一個進程可獲得連接,如果每次喚醒的進程數(shù)目太多,會影響一部分系統(tǒng)性能。在Nginx服務器的多進程下,就可能出現(xiàn)這個問題,為了解決這個問題,Nginx配置了包含這樣一條指令accept_mutex,當其設置為開啟的時候,將會對多個Nginx進程接受連接進行序列化,防止多個進程對連接的爭搶。當服務器連接數(shù)不多時,開啟這個參數(shù)會讓負載有一定程度的降低。但是當服務器的吞吐量很大時,為了效率,請關閉這個參數(shù);并且關閉這個參數(shù)的時候也可以讓請求在多個worker間的分配更均衡。所以我們設置accept_mutex off;
multi_accept
sendfile
使用開啟或關閉是否使用sendfile()傳輸文件,普通應用應該設為on,下載等IO重負荷的應用應該設為off,因為大文件不適合放到buffer中。
傳統(tǒng)文件傳輸中(read/write方式)在實現(xiàn)上3其實是比較復雜的,需要經(jīng)過多次上下文切換,當需要對一個文件傳輸時,傳統(tǒng)方式是:
調(diào)用read函數(shù),文件數(shù)據(jù)被copy到內(nèi)核緩沖區(qū)
read函數(shù)返回,數(shù)據(jù)從內(nèi)核緩沖區(qū)copy到用戶緩沖區(qū)
write函數(shù)調(diào)用,將文件數(shù)據(jù)從用戶緩沖區(qū)copy到內(nèi)核與socket相關的緩沖區(qū)
數(shù)據(jù)從socket緩沖區(qū)copy到相關協(xié)議引擎
從上面可以看出來,傳統(tǒng)readwrite進行網(wǎng)絡文件傳輸?shù)姆绞?,在過程中經(jīng)歷了四次copy操作。
硬盤->內(nèi)核buffer->用戶buffer->socket相關緩沖區(qū)->協(xié)議引擎
而sendfile系統(tǒng)調(diào)用則提供了一種減少多次copy,提高文件傳輸性能的方法。流程如下:
sendfile系統(tǒng)效用,文件數(shù)據(jù)被copy至內(nèi)核緩沖區(qū)
記錄數(shù)據(jù)文職和長度相關的數(shù)據(jù)保存到socket相關緩存區(qū)
實際數(shù)據(jù)由DMA模塊直接發(fā)送到協(xié)議引擎
tcp_nopush
sendfile為on時這里也應該設為on,數(shù)據(jù)包會累積一下再一起傳輸,可以提高一些傳輸效率。
tcp_nodelay
小的數(shù)據(jù)包不等待直接傳輸。默認為on。
看上去是和tcp_nopush相反的功能,但是兩邊都為on時nginx也可以平衡這兩個功能的使用。
keepalive_timeout
HTTP連接的持續(xù)時間。設的太長會使無用的線程變的太多。這個根據(jù)自己服務器訪問數(shù)量、處理速度以及網(wǎng)絡狀況方面考慮。
send_timeout
設置Nginx服務器響應客戶端的超時時間,這個超時時間只針對兩個客戶端和服務器建立連接后,某次活動之間的時間,如果這個時間后,客戶端沒有任何活動,Nginx服務器將關閉連接,將其設置為10s,Nginx與客戶端建立連接后,某次會話中服務器等待客戶端響應超過10s,就會自動關閉。
types_hash_max_size
types_hash_max_size影響散列表的沖突率。types_hash_max_size越大,就會消耗更多的內(nèi)存,但散列key的沖突率會降低,檢索速度就更快。types_hash_max_size越小,消耗的內(nèi)存就越小,但散列key的沖突率可能上升。
client_header_buffer_size
該指令用于設置Nginx服務器允許的客戶端請求頭部的緩沖區(qū)大小,默認為1KB,此指令的賦值可以根據(jù)系統(tǒng)分頁大小來設置,分頁大小可以用以下命令獲取getconf PAGESIZE。
client_max_body_size 8m
客戶端上傳的body的最大值。超過最大值就會發(fā)生413(Request Entity Too Large)錯誤。默認為1m,最好根據(jù)自己的情況改大一點。
gzip on
啟用gzip,對響應數(shù)據(jù)進行在線實時壓縮,減少數(shù)據(jù)傳輸量。
gzip_disable
Nginx服務器在響應這些種類的客戶端請求時,不使用Gzip功能緩存應用數(shù)據(jù),gzip_disable "msie6"對IE6瀏覽器的數(shù)據(jù)不進行GZIP壓縮。
gzip_min_length
Gzip壓縮功能對大數(shù)據(jù)的壓縮效果明顯,但是如果壓縮很小的數(shù)據(jù),可能出現(xiàn)越壓縮數(shù)據(jù)量越大的情況,因此應該根據(jù)相應頁面的大小,選擇性開啟或者關閉Gzip功能。建議將值設置為1KB。
gzip_comp_level
設置壓縮程度,包括級別1到級別9,級別1表示壓縮程度最低,壓縮效率最高;級別9表示壓縮程度最高,壓縮效率最低,最費時間。
gzip_vary
用于設置在使用Gzip功能時是否發(fā)送帶有“Vary:Accept-Encoding”頭域的響應頭部,該頭域的主要功能是告訴接收方發(fā)送的數(shù)據(jù)經(jīng)過了壓縮處理,開啟后端效果是在響應頭部Accept-Encoding: gzip,對于本身不支持Gzip的壓縮的客戶端瀏覽器是有用的。
gzip_buffers;
該指令用于設置Gzip壓縮文件使用存儲空間的大小,
語法gzip_buffers number size;number,指定Nginx服務器需要向系統(tǒng)申請存儲空間的個數(shù),size,指定每個緩存空間的大小。根據(jù)配置項,Nginx服務器在對響應輸出數(shù)據(jù)進行Gzip壓縮時需向系統(tǒng)申請numbersize大小的空間用于存儲壓縮數(shù)據(jù)。默認情況下,numbersize的值為128,其中size的值為系統(tǒng)內(nèi)存頁一頁的大小,用getconf PAGESIZE來獲取
gunzip_static
開啟時,如果客戶端瀏覽器不支持Gzip處理,Nginx服務器將返回解壓后的數(shù)據(jù),如果客戶端瀏覽器支持Gzip處理,Nginx服務器忽略該指令設置。仍然返回壓縮數(shù)據(jù)。
gzip_types
Nginx服務器可以根據(jù)MIME類型選擇性開啟Gzip壓縮功能。該指令涌來設置MIME類型。
作者:flyingpang
鏈接:https://segmentfault.com/a/1190000011405320
附上自己項目的nginx.conf:
user nginx nginx;
worker_processes auto;
worker_cpu_affinity auto;
error_log /data/logs/nginx/nginx_error.log crit;
pid logs/nginx.pid;
#Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;
events {
use epoll;
multi_accept on;
worker_connections 51200;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$http_x_forwarded_for $remote_addr [$time_local] $server_name $request '
'$status $body_bytes_sent $request_time $upstream_addr $upstream_status $upstream_response_time $http_referer $http_user_agent';
access_log /data/logs/nginx/access.log main;
error_log /data/logs/nginx/error.log;
server_names_hash_bucket_size 128;
client_header_buffer_size 64k;
#large_client_header_buffers 6 4k;
client_max_body_size 50m;
server_tokens off;
sendfile on;
tcp_nopush on;
keepalive_requests 100000;
#keepalive_timeout 30;
#send_timeout 10;
tcp_nodelay on;
#proxy_connect_timeout 300s;
#proxy_read_timeout 300s;
#proxy_send_timeout 300s;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
gzip on;
gzip_min_length 1k;
gzip_buffers 1024 4k;
gzip_http_version 1.0;
gzip_comp_level 4;
gzip_proxied any;
#gzip_proxied no_last_modified;
gzip_types text/plain application/javascript application/x-javascript application/json text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/jpg;
gzip_vary on;
include vhost/*.conf;
}