1、知識準備
● 在nginx優化中有個常常須要設置的參數,tcp_nodelay
● 該參數最核心的功能,就是把小包組成成大包,提升帶寬利用率也就是著名的nagle算法
● tcp協議中,有一個現象:應用層數據可能很低(好比1個字節),而傳輸層開銷有40字節(20字節的IP頭+20字節的TCP頭)。這種狀況下大部分都是控制包的傳輸,既加大了帶寬的消耗,帶寬利用率也不高
● nagle算法就是爲了解決這個問題。在發出去的數據還未被確認以前,或者說尚未收到對端的ack以前,新生成的小包是不容許被髮送的,必需要湊滿一個MSS或者等到收到確認後再發送,直至超時node
2、環境準備
組件 | 版本 |
---|---|
OS | Ubuntu 18.04.1 LTS |
docker | 18.06.0-ce |
客戶端 : 192.168.17.171
服務端 : 192.168.17.173nginx
3、打開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; }
啓動容器docker
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端口的流量:apache
root@k8s-node2:/tmp# tcpdump -i ens3 port 80 -afexnnvv -w nginx_ab.cap
在192.168.17.171,使用ab壓測工具對該端口進行放量網絡
注意:必須使用 -k 參數,使用keepalived模式下才能模擬出nagle算法app
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左右tcp
咱們來看下抓包信息,使用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 | | | +---------------------------------> | | | | | | | | | +-------+ +--------+
4、關閉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 | | | +---------------------------------> | | | | | | | | | +-------+ +--------+
5、小結
● 本文復現了經典的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
,會增長小包的數量,可是能夠提升響應速度。在及時性高的通訊場景中應該會有不錯的效果
至此,本文結束 在下才疏學淺,有撒湯漏水的,請各位不吝賜教...