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
,会增长小包的数量,可是能够提升响应速度。在及时性高的通讯场景中应该会有不错的效果
至此,本文结束 在下才疏学浅,有撒汤漏水的,请各位不吝赐教...