一、TCP_NODELAY
你怎麼能夠強制 socket 在它的緩衝區裏發送數據?
一個解決方案是 TCP 堆棧的 TCP_NODELAY選項。這樣就可使緩衝區中的數據當即發送出去。php
Nginx的 TCP_NODELAY 選項使得在打開一個新的 socket 時增長了TCP_NODELAY選項。但這時會形成一種狀況:
終端應用程序每產生一次操做就會發送一個包,而典型狀況下一個包會擁有一個字節的數據以及40個字節長的包頭,因而產生4000%的過載,很輕易地就能令網絡發生擁塞。爲了不這種狀況,TCP堆棧實現了等待數據 0.2秒鐘,所以操做後它不會發送一個數據包,而是將這段時間內的數據打成一個大的包。這一機制是由Nagle算法保證。html
Nagle化後來成了一種標準而且當即在因特網上得以實現。它如今已經成爲默認配置了,但有些場合下把這一選項關掉也是合乎須要的。如今假設某個應用程序發出了一個請求,但願發送小塊數據。咱們能夠選擇當即發送數據或者等待產生更多的數據而後再一次發送兩種策略。
若是咱們立刻發送數據,那麼交互性的以及客戶/服務器型的應用程序將極大地受益。若是請求當即發出那麼響應時間也會快一些。以上操做能夠經過設置套接字的 TCP_NODELAY = on 選項來完成,這樣就禁用了Nagle 算法。(不須要等待0.2s)前端
二、TCP_NOPUSH
在 nginx 中,tcp_nopush 配置和 tcp_nodelay 「互斥」。它能夠配置一次發送數據的包大小。也就是說,它不是按時間累計 0.2 秒後發送包,而是當包累計到必定大小後就發送。java
注:在 nginx 中,tcp_nopush 必須和 sendfile 搭配使用。node
三、sendfile
如今流行的web 服務器裏面都提供 sendfile選項用來提升服務器性能,那到底 sendfile是什麼,怎麼影響性能的呢?
sendfile其實是 Linux2.0+之後的推出的一個系統調用,web服務器能夠經過調整自身的配置來決定是否利用 sendfile這個系統調用。先來看一下不用 sendfile的傳統網絡傳輸過程:
read(file,tmp_buf, len);
write(socket,tmp_buf, len);nginx
硬盤 >> kernel buffer >> user buffer>> kernel socket buffer >>協議棧web
1)通常來講一個網絡應用是經過讀硬盤數據,而後寫數據到socket 來完成網絡傳輸的。上面2行用代碼解釋了這一點,不過上面2行簡單的代碼掩蓋了底層的不少操做。來看看底層是怎麼執行上面2行代碼的:redis
上面4個步驟有4次上下文切換,有4次拷貝,咱們發現若是能減小切換次數和拷貝次數將會有效提高性能。在kernel2.0+ 版本中,系統調用 sendfile() 就是用來簡化上面步驟提高性能的。sendfile() 不但能減小切換次數並且還能減小拷貝次數。算法
2)再來看一下用 sendfile()來進行網絡傳輸的過程:
sendfile(socket,file, len);後端
硬盤 >> kernel buffer (快速拷貝到kernelsocket buffer) >>協議棧
步驟減小了,切換減小了,拷貝減小了,天然性能就提高了。這就是爲何說在Nginx 配置文件裏打開 sendfile on 選項能提升 web server性能的緣由。
綜上,這三個參數都應該配置成on:sendfile on; tcp_nopush on; tcp_nodelay on;
當使用nginx做爲反向代理時,爲了支持長鏈接,須要作到兩點:
一、保持和client的長鏈接:
默認狀況下,nginx已經自動開啓了對client鏈接的keep alive支持(同時client發送的HTTP請求要求keep alive)。通常場景能夠直接使用,可是對於一些比較特殊的場景,仍是有必要調整個別參數(keepalive_timeout和keepalive_requests)。
1 2 3 4 |
http { keepalive_timeout 120s 120s; keepalive_requests 10000; } |
1)keepalive_timeout
語法:
keepalive_timeout timeout [header_timeout];
注:keepalive_timeout默認75s,通常狀況下也夠用,對於一些請求比較大的內部服務器通信的場景,適當加大爲120s或者300s;
2)keepalive_requests:
keepalive_requests指令用於設置一個keep-alive鏈接上能夠服務的請求的最大數量,當最大請求數量達到時,鏈接被關閉。默認是100。這個參數的真實含義,是指一個keep alive創建以後,nginx就會爲這個鏈接設置一個計數器,記錄這個keep alive的長鏈接上已經接收並處理的客戶端請求的數量。若是達到這個參數設置的最大值時,則nginx會強行關閉這個長鏈接,逼迫客戶端不得不從新創建新的長鏈接。
大多數狀況下當QPS(每秒請求數)不是很高時,默認值100湊合夠用。可是,對於一些QPS比較高(好比超過10000QPS,甚至達到30000,50000甚至更高) 的場景,默認的100就顯得過低。
簡單計算一下,QPS=10000時,客戶端每秒發送10000個請求(一般創建有多個長鏈接),每一個鏈接只能最多跑100次請求,意味着平均每秒鐘就會有100個長鏈接所以被nginx關閉。一樣意味着爲了保持QPS,客戶端不得不每秒中從新新建100個鏈接。所以,就會發現有大量的TIME_WAIT的socket鏈接(即便此時keep alive已經在client和nginx之間生效)。所以對於QPS較高的場景,很是有必要加大這個參數,以免出現大量鏈接被生成再拋棄的狀況,減小TIME_WAIT。
二、保持和server的長鏈接:
爲了讓nginx和後端server(nginx稱爲upstream)之間保持長鏈接,典型設置以下:(默認nginx訪問後端都是用的短鏈接(HTTP1.0),一個請求來了,Nginx 新開一個端口和後端創建鏈接,後端執行完畢後主動關閉該連接)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
http { upstream BACKEND { server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s; keepalive 300; // 這個很重要! } server { listen 8080 default_server; server_name ""; location / { proxy_pass http://BACKEND; proxy_set_header Host $Host; proxy_set_header x-forwarded-for $remote_addr; proxy_set_header X-Real-IP $remote_addr; add_header Cache-Control no-store; add_header Pragma no-cache; proxy_http_version 1.1; // 這兩個最好也設置 proxy_set_header Connection ""; } } } |
1)location中有兩個參數須要設置:
1 2 3 4 5 6 7 8 |
http { server { location / { proxy_http_version 1.1; // 這兩個最好也設置 proxy_set_header Connection ""; } } } |
HTTP協議中對長鏈接的支持是從1.1版本以後纔有的,所以最好經過proxy_http_version指令設置爲」1.1」;
而」Connection」 header應該被清理。清理的意思,個人理解,是清理從client過來的http header,由於即便是client和nginx之間是短鏈接,nginx和upstream之間也是能夠開啓長鏈接的。這種狀況下必須清理來自client請求中的」Connection」 header。
2)upstream中的keepalive設置:
此處keepalive的含義不是開啓、關閉長鏈接的開關;也不是用來設置超時的timeout;更不是設置長鏈接池最大鏈接數。官方解釋:
咱們先假設一個場景: 有一個HTTP服務,做爲upstream服務器接收請求,響應時間爲100毫秒。若是要達到10000 QPS的性能,就須要在nginx和upstream服務器之間創建大約1000條HTTP鏈接。nginx爲此創建鏈接池,而後請求過來時爲每一個請求分配一個鏈接,請求結束時回收鏈接放入鏈接池中,鏈接的狀態也就更改成idle。咱們再假設這個upstream服務器的keepalive參數設置比較小,好比常見的10.
A、假設請求和響應是均勻而平穩的,那麼這1000條鏈接應該都是一放回鏈接池就當即被後續請求申請使用,線程池中的idle線程會很是的少,趨進於零,不會形成鏈接數量反覆震盪。
B、顯示中請求和響應不可能平穩,咱們以10毫秒爲一個單位,來看鏈接的狀況(注意場景是1000個線程+100毫秒響應時間,每秒有10000個請求完成),咱們假設應答始終都是平穩的,只是請求不平穩,第一個10毫秒只有50,第二個10毫秒有150:
C、一樣,若是假設相應不均衡也會出現上面的鏈接數波動狀況。
形成鏈接數量反覆震盪的一個推手,就是這個keepalive 這個最大空閒鏈接數。畢竟鏈接池中的1000個鏈接在頻繁利用時,出現短期內多餘10個空閒鏈接的機率實在過高。所以爲了不出現上面的鏈接震盪,必須考慮加大這個參數,好比上面的場景若是將keepalive設置爲100或者200,就能夠很是有效的緩衝請求和應答不均勻。
總結:
keepalive 這個參數必定要當心設置,尤爲對於QPS比較高的場景,推薦先作一下估算,根據QPS和平均響應時間大致能計算出須要的長鏈接的數量。好比前面10000 QPS和100毫秒響應時間就能夠推算出須要的長鏈接數量大概是1000. 而後將keepalive設置爲這個長鏈接數量的10%到30%。比較懶的同窗,能夠直接設置爲keepalive=1000之類的,通常都OK的了。
三、綜上,出現大量TIME_WAIT的狀況
1)致使 nginx端出現大量TIME_WAIT的狀況有兩種:
2)致使後端server端出現大量TIME_WAIT的狀況:
nginx沒有打開和後端的長鏈接,即:沒有設置proxy_http_version 1.1;和proxy_set_header Connection 「」;從而致使後端server每次關閉鏈接,高併發下就會出現server端出現大量TIME_WAIT
一、配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
server { listen 80 default_server; listen 443 ssl; server_name toutiao.iqiyi.com toutiao.qiyi.domain m.toutiao.iqiyi.com; root /data/none; index index.php index.html index.htm; ###ssl settings start ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_certificate /usr/local/nginx/conf/server.pem; ssl_certificate_key /usr/local/nginx/conf/server.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP; ssl_prefer_server_ciphers on; ###ssl settings end … |
二、性能比較:
經過https訪問Nginx通常會比http訪問慢30%(https方式訪問主要是耗Nginx服務器的cpu)經過下面實驗驗證:
統計qps時,每次清空nginx日誌,而後加壓,執行完畢後使用以下命令查看qps:
1 2 |
# cat log.2.3000https | grep '/api/news/v1/info?newsId=' | awk '{print$3}'| uniq | wc -l 37 |
注:不能持續加壓,不然無限加大壓力後每每是後端java服務出現瓶頸,致使返回給nginx的響應變慢,從而使得nginx壓力變小。
三、優化: Nginx默認使用DHE算法來產生密匙,該加密算法效率很低。能夠經過以下命令,刪掉了kEDH算法。 ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;