nginx参数tcp_nopush和tcp_nodelay

2023-11-04

参数说明

你的数据传输并不需要总是准确地遵守某一选项或者其它选择。在那种情况下,你可能想要采取更为灵活的措施来控制网络连接:

在发送一系列当作单一消息的数据之前设置TCP_CORK,而且在发送应立即发出的短消息之前设置TCP_NODELAY。

如果需要提供网络的传输效率,应该减少小包的传输,使用TCP_CORK来做汇总传输,在利用sendfile来提高效率;

但如果是交互性的业务,那应该让任意小包可以快速传输,关闭Nagle算法,提高包的传输效率。

TCP_CORK优化了传输的bits效率,tcp_nodelay优化了传输的packet效率。

语法: tcp_nodelay on | off;

默认值:

tcp_nodelay on;

上下文: http, server, location

开启或关闭nginx使用TCP_NODELAY选项的功能。 这个选项仅在将连接转变为长连接的时候才被启用。(在upstream发送响应到客户端时也会启用)。

语法: tcp_nopush on | off;

默认值:

tcp_nopush off;

上下文: http, server, location

开启或者关闭nginx在FreeBSD上使用TCP_NOPUSH套接字选项, 在Linux上使用TCP_CORK套接字选项。 选项仅在使用sendfile的时候才开启。

首先了解两个概念

Nagle:

假如需要频繁的发送一些小包数据,比如说1个字节,以IPv4为例的话,则每个包都要附带40字节的头,也就是说,总计41个字节的数据里,其中只有1个字节是我们需要的数据。

为了解决这个问题,出现了Nagle算法。它规定:如果包的大小满足MSS,那么可以立即发送,否则数据会被放到缓冲区,等到已经发送的包被确认了之后才能继续发送。

通过这样的规定,可以降低网络里小包的数量,从而提升网络性能。

DelayedAcknowledgment:

假如需要单独确认每一个包的话,那么网络中将会充斥着无数的ACK,从而降低了网络性能。

为了解决这个问题,DelayedAcknowledgment规定:不再针对单个包发送ACK,而是一次确认两个包,或者在发送响应数据的同时捎带着发送ACK,又或者触发超时时间后再发送ACK。

通过这样的规定,可以降低网络里ACK的数量,从而提升网络性能。

Nagle和DelayedAcknowledgment是如何影响性能的

Nagle和DelayedAcknowledgment虽然都是好心,但是它们在一起的时候却会办坏事。

如果一个 TCP 连接的一端启用了 Nagle‘s Algorithm,而另一端启用了 TCP Delayed Ack,而发送的数据包又比较小,则可能会出现这样的情况:

发送端在等待接收端对上一个packet 的 Ack 才发送当前的 packet,而接收端则正好延迟了此 Ack 的发送,那么这个正要被发送的 packet 就会同样被延迟。

当然 Delayed Ack 是有个超时机制的,而默认的超时正好就是 40ms。 

现代的 TCP/IP 协议栈实现,默认几乎都启用了这两个功能,你可能会想,按我上面的说法,当协议报文很小的时候,岂不每次都会触发这个延迟问题?

事实不是那样的。仅当协议的交互是发送端连续发送两个 packet,然后立刻 read 的 时候才会出现问题。

现在让我们假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。

如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。

例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相比就会比较低,而且,如果请求立即发出那么响应时间也会快一些。

以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle 算法。 

另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。

应用Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。

假设客户端的请求发生需要等待服务端的应答后才能继续发生下一包,即串行执行,

好比在用ab性能测试时只有一个并发做10k的压力测试,测试地址返回的内容只有Hello world;ab发出的request需要等待服务器返回response时,才能发生下一个request;

此时ab只会发生一个get请求,请求的相关内容包含在header中;而服务器需要返回两个数据,一个是response头,另一个是html body;

服务器发送端发送的第一个 write 是不会被缓冲起来,而是立刻发送的(response header),

这时ab接收端收到对应的数据,但它还期待更多数据(html)才进行处理,所以不会往回发送数据,因此也没机会把 Ack 给带回去,根据Delayed Ack 机制, 这个 Ack 会被 Hold 住。

这时服务器发送端发送第二个包,而队列里还有未确认的数据包(response header),这个 packet(html) 会被缓冲起来。

此时,服务器发送端在等待ab接收端的 Ack;ab接收端则在 Delay 这个 Ack,所以都在等待,

直到ab接收端 Deplayed Ack 超时(40ms),此 Ack 被发送回去,发送端缓冲的这个 packet(html) 才会被真正送到接收端,

此时ab才接受到完整的数据,进行对应的应用层处理,处理完成后才继续发生下一个request,因此服务器端才会在read时出现40ms的阻塞。

tcp_nodelay为什么只在keep-alive才启作用

TCP中的Nagle算法默认是启用的,但是它并不是适合任何情况,对于telnet或rlogin这样的远程登录应用的确比较适合(原本就是为此而设计),但是在某些应用场景下我们却又需要关闭它。

在Apache对HTTP持久连接(Keep-Alive,Prsistent-Connection)处理时凸现的奇数包&结束小包问题(The Odd/Short-Final-Segment Problem),

这是一个并的关系,即问题是由于已有奇数个包发出,并且还有一个结束小包(在这里,结束小包并不是指带FIN旗标的包,而是指一个HTTP请求或响应的结束包)等待发出而导致的。

我们来看看具体的问题详情,以3个包+1个结束小包为例,可能发生的发包情况:

服务器向客户端发出两个大包;客户端在接受到两个大包时,必须回复ack;

接着服务器向客户端发送一个中包或小包,但服务器由于Delayed Acknowledgment并没有马上ack;

由于发生队列中有未被ack的包,因此最后一个结束的小包被阻塞等待。

最后一个小包包含了整个响应数据的最后一些数据,所以它是结束小包,如果当前HTTP是非持久连接,那么在连接关闭时,最后这个小包会立即发送出去,这不会出现问题;

但是,如果当前HTTP是持久连接(非pipelining处理,pipelining仅HTTP 1.1支持,nginx目前对pipelining的支持很弱,它必须是前一个请求完全处理完后才能处理后一个请求),

即进行连续的Request/Response、Request/Response、…,处理,那么由于最后这个小包受到Nagle算法影响无法及时的发送出去

(具体是由于客户端在未结束上一个请求前不会发出新的request数据,导致无法携带ACK而延迟确认,进而导致服务器没收到客户端对上一个小包的的确认导致最后一个小包无法发送出来),

导致第n次请求/响应未能结束,从而客户端第n+1次的Request请求数据无法发出。

在http长连接中,服务器的发生类似于:Write-Write-Read,即返回response header、返回html、读取下一个request

而在http短连接中,服务器的发生类似于:write-read-write-read,即返回处理结果后,就主动关闭连接,短连接中的close之前的小包会立即发生,不会阻塞 

我的理解是这样的:因为第一个 write 不会被缓冲,会立刻到达接收端,如果是 write-read-write-read 模式,此时接收端应该已经得到所有需要的数据以进行下一步处理。

接收端此时处理完后发送结果,同时也就可以把上一个packet 的 Ack 可以和数据一起发送回去,不需要 delay,从而不会导致任何问题。 

我做了一个简单的试验,注释掉了 HTTP Body 的发送,仅仅发送 Headers, Content-Length 指定为 0。

这样就不会有第二个 write,变成了 write-read-write-read 模式。此时再用 ab 测试,果然没有 40ms 的延迟了。

因此在短连接中并不存在小包阻塞的问题,而在长连接中需要做tcp_nodelay开启。

那tcp_nopush又是什么?

TCP_CORK选项的功能类似于在发送数据管道出口处插入一个“塞子”,使得发送数据全部被阻塞,直到取消TCP_CORK选项(即拔去塞子)或被阻塞数据长度已超过MSS才将其发送出去。

选项TCP_NODELAY是禁用Nagle算法,即数据包立即发送出去,而选项TCP_CORK与此相反,可以认为它是Nagle算法的进一步增强,即阻塞数据包发送,

具体点说就是:TCP_CORK选项的功能类似于在发送数据管道出口处插入一个“塞子”,使得发送数据全部被阻塞,

直到取消TCP_CORK选项(即拔去塞子)或被阻塞数据长度已超过MSS才将其发送出去。

举个对比示例,比如收到接收端的ACK确认后,Nagle算法可以让当前待发送数据包发送出去,即便它的当前长度仍然不够一个MSS,

但选项TCP_CORK则会要求继续等待,这在前面的tcp_nagle_check()函数分析时已提到这一点,即如果包数据长度小于当前MSS &&((加塞 || …)|| …),那么缓存数据而不立即发送:

在TCP_NODELAY模式下,假设有3个小包要发送,第一个小包发出后,接下来的小包需要等待之前的小包被ack,在这期间小包会合并,直到接收到之前包的ack后才会发生;

而在TCP_CORK模式下,第一个小包都不会发生成功,因为包太小,发生管道被阻塞,同一目的地的小包彼此合并后组成一个大于mss的包后,才会被发生

TCP_CORK选项“堵塞”特性的最终目的无法是为了提高网络利用率,既然反正是要发一个数据包(零窗口探测包),

如果有实际数据等待发送,那么干脆就直接发送一个负载等待发送数据的数据包岂不是更好?

我们已经知道,TCP_CORK选项的作用主要是阻塞小数据发送,所以在nginx内的用处就在对响应头的发送处理上。

一般而言,处理一个客户端请求之后的响应数据包括有响应头和响应体两部分,那么利用TCP_CORK选项就能让这两部分数据一起发送:

假设我们需要等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。

应用Nagle算法在这种情况下就会产生问题。因为TCP_NODELAY在发生小包时不再等待之前的包有没有ack,网络中会存在较多的小包,但这会影响网络的传输能力;

但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。

下面就让我们仔细分析下其工作原理。 

假设应用程序使用sendfile()函数来转移大量数据。应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。

典型情况下报头很小,而且套接字上设置了TCP_NODELAY。有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求对方确认。

这样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。

但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入“塞子”)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地通过包传输出去。

当数据传输完成时,最好取消TCP_CORK 选项设置给连接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络连接同等重要。 

总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。

能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作。

参考文献:

https://www.cnblogs.com/68xi/p/13418497.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

nginx参数tcp_nopush和tcp_nodelay 的相关文章

  • 适用于 Linux 的轻量级 IDE [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何使用 Cloud Init 挂载未格式化的 EBS 卷

    Context 我正在使用https wiki jenkins io display JENKINS Amazon EC2 Plugin https wiki jenkins io display JENKINS Amazon EC2 Pl
  • NGINX 返回 405 不允许使用 POST 方法

    我有这个default conf server listen 443 ssl root etc nginx json server name myserver com ssl certificate etc ssl certs server
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • Kubernetes - 一个 Ingress 中的多个配置

    我在同一个 Kubernetes 集群中运行不同的应用程序 我希望多个域能够访问我的 Kubernetes 集群 并根据域进行重定向 对于每个域 我想要不同的注释 配置 如果没有注释 我的入口部署如下 apiVersion networki
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • C 语言的符号表

    我目前正在开发一种执行模式匹配的静态分析工具 我在用Flex https github com westes flex生成词法分析器 我编写了代码来管理符号表 我不太有经验C 所以我决定将符号表实现为线性链表 include
  • 如何根据标签将单个 XML 文件拆分为多个

    我有一个带有标签的 XML 文件 我想像这样分割文件
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 添加文件时运行 shell 命令

    我的 Linux 机器上有一个名为 images 的文件夹 该文件夹连接到一个网站 该网站的管理员可以向该网站添加图片 但是 当添加图片时 我想要一个命令来运行调整目录中所有图片的大小 简而言之 我想知道当新文件添加到特定位置时如何使服务器
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 与 pthread 的进程间互斥

    我想使用一个互斥体 它将用于同步对两个不同进程共享的内存中驻留的某些变量的访问 我怎样才能做到这一点 执行该操作的代码示例将非常感激 以下示例演示了 Pthread 进程间互斥体的创建 使用和销毁 将示例推广到多个进程作为读者的练习 inc
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • 这种文件锁定方法可以接受吗?

    我们有 10 个 Linux 机器 每周必须运行 100 个不同的任务 这些计算机主要在我们晚上在家时执行这些任务 我的一位同事正在开发一个项目 通过使用 Python 自动启动任务来优化运行时间 他的程序将读取任务列表 抓取一个打开的任务
  • 我什么时候应该编写 Linux 内核模块?

    有些人出于某种原因想要将 Linux 中的代码从用户空间移动到内核空间 很多时候 原因似乎是代码应该具有特别高的优先级 或者只是 内核空间更快 这对我来说似乎很奇怪 我什么时候应该考虑编写内核模块 有一套标准吗 我怎样才能激励将代码保存在
  • node.js 本身还是 nginx 前端来提供静态文件?

    是否有更快的基准或比较 将 nginx 放在节点前面并让它直接提供静态文件或仅使用节点并使用它提供静态文件 nginx 解决方案似乎对我来说更易于管理 有什么想法吗 我不得不不同意这里的答案 虽然 Node 可以做得很好 但如果配置正确 n
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的

随机推荐

  • 在Mac上配置Vue开发环境

    因为Vue是NodeJS的模块 要想使用Vue需要先安装NodeJS 在Mac中安装NodeJS通过brew包管理器就会很方便 因为访问源速度问题建议使用5 阿里 Homebrew开源项目地址 https gitee com cunkai
  • 抱抱脸(hugging face)教程-中文翻译-分享一个模型

    分享一个模型 最后两个教程展示了如何使用 PyTorch Keras 和 Accelerate 优化分布式设置的模型 下一步就是把你的模型公之于众 我们相信公开分享知识和资源 使人工智能大众化 我们鼓励你考虑与社区分享你的模式 以帮助其他人
  • 史上最全!Selenium 录制脚本+八种元素定位方式+具体代码演示

    话不多说 先附上练习的所有代码链接 link 文章目录 引言 什么是自动化测试 一 selenium定义 二 使用selenium IDE录制脚本 三 元素的定位方式 1 id gt find element by id 2 name gt
  • 欧拉筛法代码及数学原理

    数学原理 首先 从 2 开始 把 2 的倍数都标记为合数 然后把下一个未标记的数 3 标记为素数 再把 3 的倍数标记为合数 接着把下一个未标记的数 5 标记为素数 再把 5 的倍数标记为合数 以此类推 直到标记到 N 为止 在标记的过程中
  • Java #{} 和 ${} 的含义及区别

    表示一个占位符 向占位符输入参数 MyBatis 会自动进行 Java 类型和 jdbc 类型的转换 且不需要考虑参数的类型 以预编译的方式传入 可以有效的防止 SQL 注入 提高系统安全性 例如 传入字符串 MyBatis 最终拼接好的
  • 学会了c语言怎么编程,三天学会C语言编程

    本文试图通过上中下三篇文章引领大家进入C语言的世界 C语言是一个非常古老 1972年发明 的语言了 想必大家都有所了解 没有了解也没关系 C语言以难学和难以使用着称 想用好C语言更是难上加难 本文不假设读者有任何其它编程语言的基础 但需要了
  • 安装或卸载Anaconda后Windows自带的cmd命令行窗口会闪退

    问题现象 Anaconda初次安装或者重装后 如果打开Windows系统自带的cmd命令窗口 会马上闪退 Win R 输入cmd就闪退 Win R 输入cmd d可以正常打开 解决方案 网上很多给出的解决方案是 按Win R 输入reged
  • Invalid configuration `x86_64-unknown-linux-gnu': machine

    checking host system type Invalid configuration x86 64 unknown linux gnu machine x86 64 unknown not recognized 在做 config
  • Gamemaker studio2经验(4)——打字机效果

    问题概述 在很多游戏中 算了实在不好意思写引言了 就直说啦 如果你是UT粉 想用gm搞搞UT的同人作品但是又无从下手 那么请看过来 对于RPG类游戏 文字交流系统是不可或缺的 但是gm的文字系统 实在有些一言难尽 那没办法 谁让gm是真爱呢
  • 本地计算机架设http服务器,Http File Server(简易Http服务器服务端)

    如果您感觉配置IIS和apache等web服务端太麻烦的话 不妨试试Http File Server Http File Server是一套简易的Http服务器服务端系统 它无需安装 运行后简单配置一下就可以开放80端口了 与传统Web服务
  • iframe嵌入本地视频或者http链接视频禁止自动播放

    本地视频禁止自动播放 http链接视频禁止自动播放 在视频链接后加上 autoplay 0即可
  • windows连网疑问

    如果我的笔记本连接一台无线AP 当我不更改SSID 而只去更改密码的时候 为什么会连不上 当我把电脑里关于这个AP的连接信息删掉之后 就能够连接上了 所以 是跟之前保存的信息有关系的 但其中的作用原理不是很清楚 不过 我想 这必定是wind
  • ubuntu16.04下teamviewer启动不显示界面

    导读 在Ubuntu下使用teamviewer的时候 通过命令行输入 teamviewer 不会出现界面 就像这样 没有显示teamviewer的界面 adminuser adminuser pc teamviewer Init Check
  • linux 系统调用

    5 1 5 如何使用系统调用 如图5 2所示 用户应用可以通过两种方式使用系统调用 第一种方式是通过C库函数 包括系统调用在C库中的封装函数和其他普通函数 图5 2 使用系统调用的两种方式 第二种方式是使用 syscall宏 2 6 18版
  • VUE路由的hash模式与history模式的区别

    hash模式url带 号 history模式不带 号 通过history api 我们丢掉了丑陋的 但是它也有个问题 不怕前进 不怕后退 就怕刷新 f5 如果后端没有准备的话 因为刷新是实实在在地去请求服务器的 不玩虚的 在hash模式下
  • 使用fio测试磁盘I/O性能

    测试准备 工具 fio Flexible IO Tester 官方网站 http freecode com projects fio http brick kernel dk snaps 注意 性能测试建议直接通过写裸盘的方式进行测试 会得
  • JavaScript之js对象终极序列化(可序列化函数)

    案例官方地址 http www rainx org 2017 01 04 javascript js E5 AF B9 E8 B1 A1 E7 BB 88 E6 9E 81 E5 BA 8F E5 88 97 E5 8C 96 你是否遇到了
  • PTA 习题3.6 一元多项式的乘法与加法运算

    文章目录 题目 1 加法多项式 2 乘法多项式 代码 题目 设计函数分别求两个一元多项式的乘积与和 输入格式 输入分2行 每行分别先给出多项式非零项的个数 再以指数递降方式输入一个多项式非零项系数和指数 绝对值均为不超过1000的整数 数字
  • win11安装linux子系统配置C++开发环境的系列教程

    本期主要记录在Win11系统下安装ubuntu20 04LTS并完成相关配置 在整个过程中 博主也踩过了不少坑 参考了许多优秀作者的资源 将配置过程整理总结如下 第一步 配置安装ubuntu 其实主要做的事情就是 调整 Win 配置 下载配
  • nginx参数tcp_nopush和tcp_nodelay

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