仔細看參數--NGINX之tcp_nodelay


一、知識准備

● 在nginx優化中有個經常需要設置的參數,tcp_nodelay
● 該參數最核心的功能,就是把小包組成成大包,提高帶寬利用率也就是著名的nagle算法
● tcp協議中,有一個現象:應用層數據可能很低(比如1個字節),而傳輸層開銷有40字節(20字節的IP頭+20字節的TCP頭)。這種情況下大部分都是控制包的傳輸,既加大了帶寬的消耗,帶寬利用率也不高
● nagle算法就是為了解決這個問題。在發出去的數據還未被確認之前,或者說還沒有收到對端的ack之前,新生成的小包是不允許被發送的,必須要湊滿一個MSS或者等到收到確認后再發送,直至超時


二、環境准備

組件 版本
OS Ubuntu 18.04.1 LTS
docker 18.06.0-ce

客戶端 : 192.168.17.171
服務端 : 192.168.17.173


三、打開nagle算法

192.168.17.173,先准備一個nginx配置文件,並且打開nagle算法,設置tcp_nodelay off;

root@k8s-node2:/tmp# more nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    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  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nodelay off;

    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;
}

啟動容器

root@k8s-node2:/tmp# docker run -d --name nginx_delay -v /tmp/nginx.conf:/etc/nginx/nginx.conf -p 80:80 nginx:latest
6b7d5a5d3c3ed021fed6847d138837754c5732979d1c829ec62107ec80440db8
root@k8s-node2:/tmp# docker ps | grep nginx_delay
6b7d5a5d3c3e        nginx:latest                                                        "nginx -g 'daemon of…"   7 seconds ago       Up 6 seconds        0.0.0.0:80->80/tcp   nginx_delay

首先使用tcpdump抓取本機的80端口的流量:

root@k8s-node2:/tmp# tcpdump -i ens3 port 80 -afexnnvv -w nginx_ab.cap

在192.168.17.171,使用ab壓測工具對該端口進行放量

注意:必須使用 -k 參數,使用keepalived模式下才能模擬出nagle算法

root@k8s-node2:~# ab -n 1000 -c 100 -k http://127.0.0.1/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

...
Time per request:       44.619 [ms] (mean)
...

過濾掉大量信息,我們來到這個指標Time per request,無論怎么測試,平均延時總是在40ms左右

我們來看下抓包信息,使用wireshark打開

在大量的數據包中,我們先處理一下數據包,隨便選取一個syn,選取與該syn對應的tcp流

選取一個片段來分析

● 在Linux中,默認打開了延遲確認,所謂延遲確認,即不是收到每個請求都發送一次ack,而是等待一段時間,如果這段時間正好有包需要發送,就坐着“順風車”一起發出,否則超時后單獨發送。所以客戶端會等待40ms,再發送這個ack
● 由於nginx也設置了nagle算法,如果沒有收到ack,它會等着包的到來,所以就會呈現這個樣子
    (1)192.168.17.171首先發送一個http get請求(677號包)
    (2)192.167.17.173發送PSH,ACK(999號包)
    (3)此時由於Linux默認打開延遲確認,192.168.17.171會等待40ms,看看有沒有“順風車”;而192.168.17.173上的nginx由於關閉了tcp_nodelay,它也會等待着ack的到來再回應
    (4)40ms過后,192.168.17.171沒有等到“順風車”,此時發送ack(1109號包)
    (5)192.168.17.173收到ack后發送了http 200(1118號包)
    (6)192.168.17.171收到數據之后發送確認ack(1127號包)

192.168.17.171:47388                        192.168.17.173:80
     +-------+                                 +--------+
     |       |       no.677  http get          |        |
     |       +--------------------------------->        |
     |       |                                 |        |
     |       |       no.999  PSH,ACK           |        |
     |       <---------------------------------+        |
     |       |                                 |        |
     |       |                                 |        |
     |       |                                 |        |
     |       |          delay 40ms             |        |
     |       |                                 |        |
     |       |                                 |        |
     |       |                                 |        |
     |       |       no.1109  ACK              |        |
     |       +--------------------------------->        |
     |       |                                 |        |
     |       |       no.1118  http 200         |        |
     |       <---------------------------------+        |
     |       |                                 |        |
     |       |       no.1127  ACK              |        |
     |       +--------------------------------->        |
     |       |                                 |        |
     |       |                                 |        |
     +-------+                                 +--------+

四、關閉nagle

只需要設置tcp_nodelay on;

root@k8s-node2:/tmp# sed -i '/tcp_nodelay/s/off/on/g' nginx.conf
root@k8s-node2:/tmp# docker rm -f nginx_delay
nginx_delay
root@k8s-node2:/tmp# docker run -d --name nginx_delay -v /tmp/nginx.conf:/etc/nginx/nginx.conf -p 80:80 nginx:latest
bac9bcf7a6e392a7a07afae165c3d5b4e3fb2fc43d3470f35802e12d1e7ae70d

再用ab測試:

root@k8s-node2:~# ab -n 1000 -c 100 -k http://127.0.0.1/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

...
Time per request:       14.285 [ms] (mean)
...

再來觀察抓包的結果:

● 由於客戶端依然打開了延遲確認,所以192.168.17.171收到數據包之后依然沒有及時回應
● 但是nginx,tcp_nodelay on,所以192.168.17.173收到數據包后會立即響應ack
● 192.168.17.171收到之后,已經有2個沒有確認的數據包了,所以會立即發送ack進行確認:
    (1)192.168.17.171首先發送一個http get請求(447號包)
    (2)192.168.17.173收到之后立即響應psh,ack(740號包)
    (3)192.168.17.173發送http 200(741號包)
    (4)192.168.17.171回應ack(742號包)

192.168.17.171:49718                        192.168.17.173:80
     +-------+                                 +--------+
     |       |       no.447  http get          |        |
     |       +--------------------------------->        |
     |       |                                 |        |
     |       |       no.740  PSH,ACK           |        |
     |       <---------------------------------+        |
     |       |                                 |        |
     |       |       no.741  http 200          |        |
     |       <---------------------------------+        |
     |       |                                 |        |
     |       |       no.742  ACK               |        |
     |       +--------------------------------->        |
     |       |                                 |        |
     |       |                                 |        |
     +-------+                                 +--------+

五、小結

● 本文復現了經典的40ms問題
● 本文中提到了2個名詞,nagle算法與延遲確認,它們看上去很相似,但是並不一樣。nagle算法是需要等到對端ack來臨,或者湊滿一個mss之后才發送數據包;而延遲確認針對的是ack,ack會等待“順風車”,如果有,就乘坐順風車發送,否則等待超時之后單獨發送
● 本文中延遲確認是Linux默認打開的功能,所以在實驗中,客戶端都會有延時確認的情況,要關閉客戶端延遲確認,需要設置setsockopt中的TCP_QUICKACK
● 本文中主要討論的是nginx的nagle算法,nagle算法完全由tcp協議的ack機制決定,如果對端ACK回復很快的話,nagle事實上不會拼接太多的數據包,雖然避免了網絡擁塞,網絡總體的利用率依然很低
● nagle算法在與延遲確認互相作用的情況下,會產生嚴重的延時效果,這是需要警惕的
● nginx中是否打開nagle算法,要取決於業務場景。比如在實驗中看到:
    (1)tcp_nodelay off,會增加通信的延時,但是會提高帶寬利用率。在高延時、數據量大的通信場景中應該會有不錯的效果
    (2)tcp_nodelay on,會增加小包的數量,但是可以提高響應速度。在及時性高的通信場景中應該會有不錯的效果



至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM