一、知识准备

● 在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

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流

nodelay_off_1.png

选取一个片段来分析

nodelay_off_2.png

● 在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

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)

...

再来观察抓包的结果:

nodelay_on_1.png

● 由于客户端依然打开了延迟确认,所以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,会增加小包的数量,但是可以提高响应速度。在及时性高的通信场景中应该会有不错的效果

至此,本文结束

在下才疏学浅,有撒汤漏水的,请各位不吝赐教...

linux tcp_nodelay,仔细看参数--NGINX之tcp_nodelay相关推荐

  1. nginx参数tcp_nopush和tcp_nodelay

    参数说明 你的数据传输并不需要总是准确地遵守某一选项或者其它选择.在那种情况下,你可能想要采取更为灵活的措施来控制网络连接: 在发送一系列当作单一消息的数据之前设置TCP_CORK,而且在发送应立即发 ...

  2. linux第三方模块参数,nginx 的第三方模块ngx_http_accesskey_module 来实现下载文件的防盗链步骤(linux系统下)...

    nginx 的第三方模块ngx_http_accesskey_module 来实现下载文件的防盗链步骤(linux系统下),安装Nginx和HttpAccessKeyModule模块(参考LNMP环境 ...

  3. 从Linux源码看Socket(TCP)的listen及连接队列

    从Linux源码看Socket(TCP)的listen及连接队列 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看 ...

  4. Linux服务器下安装配置Nginx的教程

    这篇文章主要介绍了Linux服务器下安装配置Nginx服务器的教程,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 Nginx("engine x")是一款 ...

  5. linux源码_从linux源码看epoll及epoll实战揭秘

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  6. java linux 调用32位so_从linux源码看socket(tcp)的timeout

    从linux源码看socket(tcp)的timeout 前言 网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌.在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp ...

  7. close wait 过多原因_从Linux源码看TIME_WAIT状态的持续时间

    前言 笔者一直以为在Linux下TIME_WAIT状态的Socket持续状态是60s左右.线上实际却存在TIME_WAIT超过100s的Socket.由于这牵涉到最近出现的一个复杂Bug的分析.所以, ...

  8. ip受限 linux_从linux源码看epoll及epoll实战揭秘

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  9. 从linux源码看epoll及epoll实战揭秘

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  10. 从Linux源码看Socket(TCP)的bind

    从Linux源码看Socket(TCP)的bind 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看下Server ...

最新文章

  1. QIIME 2教程. 01简介和安装 Introduction Install(2020.11开始更新)
  2. python安装某些库失败的问题解决方案
  3. Python统计在一个队列中有多少个正数,多少个负数
  4. μC-/OS II(一) PC编译环境的搭建
  5. thuderbird接收qq邮箱设置
  6. 前端学习(3112):react-hello-复习相关知识
  7. 周报_2012第11周(2012/03/11-2012/03/17)
  8. 隐藏鼠标指针_Mac鼠标光标消失怎么办?苹果电脑鼠标指针不显示的解决方法
  9. 文本处理三剑客awk的使用
  10. 基于MPI并行的VTI介质逆时偏移成像与ADCIGs提取
  11. PDF文件的加载及展示
  12. win10无法装载iso文件_装载Win10 ISO镜像文件的具体方法
  13. 微信开放平台申请方法与用途
  14. 微信测试睡眠的软件,微信小睡眠小程序使用方法
  15. 基于Java毕业设计疫情下的居民管理系统源码+系统+mysql+lw文档+部署软件
  16. 家用计算机硬件升级方案,旧电脑如何升级?旧电脑配置升级推荐方案
  17. 泛型及其使用、Stream的方法(Java小白进阶day17)
  18. 【收藏】最全计算机网络基础思维导图
  19. java int溢出,结果只会保留低32位,高位会抛弃掉
  20. 访问www.baidu.com经历了什么

热门文章

  1. STC1_FULLSCREEN_TABLE_CONTROL
  2. 精彩案例:一碗牛肉面的思考
  3. C代码编译过程,cmakelist基础步骤
  4. 第三季-第19课-消息队列编程
  5. 简易RAM的C++实现
  6. java中NULL与 的区别
  7. 操作系统复习笔记(三)
  8. NoSql数据库确实非常适合网站
  9. ios手机Safari本地服务连不上
  10. bzoj 2151 种树 —— 思路+链表