[TOC]
TCP协议中的Nagle算法
TCP中的Nagle算法
Linux下TCP延迟确认(Delayed Ack)机制导致的时延问题分析
TCP-IP详解:Delay ACK
Nagle算法为了避免网络中存在太多的小数据包,尽可能发送大的数据包。定义为在任意时刻,最多只有一个未被确认的小段。小段为小于MSS尺寸的数据块,未被确认是指数据发出去后未收到对端的ack。
Nagle算法是在网速较慢的时代的产物,目前的网络环境已经不太需要该机制,该算法在linux系统中默认关闭。
1)如果包长度达到MSS,则允许发送;
2)如果该包含有FIN,则允许发送;
3)设置了TCP_NODELAY选项,则允许发送;
4)未设置TCP_CORK选项时,若所有发出去的包均被确认,或所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送。
对于规则4),就是说要求一个TCP连接上最多只能有一个未被确认的小数据包,在该分组的确认到达之前,不能发送其他的小数据包。如果某个小分组的确认被延迟了(案例中的40ms),那么后续小分组的发送就会相应的延迟。也就是说延迟确认影响的并不是被延迟确认的那个数据包,而是后续的应答包。
tcp默认使用nagle算法,最大限度的进行缓存。
优点 :避免网络中充斥着许多小数据块,降低网络负载,减少网络拥塞,提高网络吞吐
缺点 :客户端的延迟会增加,实时性降低,不适合延时要求尽量小的场景;且对于大文件传输这种场景,会降低传输速度。
用TCP_NODELAY选项可以禁止Negale 算法。此时,应用程序向内核递交的每个数据包都会立即发送出去。需要注意的是,虽然禁止了Negale 算法,但网络的传输仍然受到TCP确认延迟机制的影响。
TCP在接收到对端的报文后,并不会立即发送ack,而是等待一段时间发送ack,以便将ack和要发送的数据一块发送。当然ack不能无限延长,否则对端会认为包超时而造成报文重传。linux采用动态调节算法来确定延时的时间。
TCP在何时发送ACK的时候有如下规定:
优点 :减少了数据段的个数,提高了发送效率
缺点 :过多的delay会拉长RTT(往返时延)
可以通过TCP_QUICKACK这个选项来启动快速ACK:
所谓的CORK就是塞子的意思,形象地理解就是用CORK将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。Cork算法与Nagle算法类似,也有人把Cork算法称呼为super-Nagle。Nagle算法提出的背景是网络因为大量小包小包而导致利用率低下产生网络拥塞,网络发生拥塞的时候性能还会进一步下降,因此Nagle算法通过ACK确认包来触发新数据包的发送(ACK确认包意味着对端已经接收到了一个数据包,即有一个数据包已经离开中间网络,此时可以在向中间网络注入一个数据包块,这称呼为self-clocking)。Cork算法则更为激进,一旦打开Cork算法,TCP不关注是否有收到ACK报文,只要当前缓存中累积的数据量不足以组成一个full-sized数据包就不会将数据包发出,直到一个RTO超时后才会把不满足一个full-sized的数据包发出去(实际上是通过一个persist timer来设置的这个RTO定时时间,persist timer超时的时候就会强制发送)。
linux中可以通过TCP_CORK选项来设置socket打开Cork算法。TCP_NODELAY选项和TCP_CORK选项在linux早期版本是互斥的,但目前最新的linux版本已经可以同时打开这两个选项了,但是TCP_CORK选项的优先级要比TCP_NODELAY选项的优先级要高。
Nagle算法和CORK算法非常类似,但是它们的着眼点不一样,Nagle算法主要避免网络因为太多的小包(协议头的比例非常之大)而拥塞,而CORK算法则是为了提高网络的利用率,使得总体上协议头占用的比例尽可能的小.如此看来这二者在避免发送小包上是一致的,在用户控制的层面上,Nagle算法完全不受用户socket的控制,你只能简单的设置TCP_NODELAY而禁用它,CORK算法同样也是通过设置或者清除TCP_CORK使能或者禁用之,然而Nagle算法关心的是网络拥塞问题,只要所有的ACK回来则发包,而CORK算法却只关心内容,在前后数据包发送间隔很短的前提下(很重要,否则内核会帮你将分散的包发出),即使你是分散发送多个小数据包,你也可以通过使能CORK算法将这些内容拼接在一个包内,如果此时用Nagle算法的话,则可能做不到这一点.
优点 :提高网络的利用率
缺点 :对实时性有影响
使用TCP_CORK参数进行配置
1.Deadline scheduler Deadline scheduler 用 deadline 算法保证对于既定的 IO 请求以最小的延迟时间,从这一点理解,对于 DSS 应用应该会是很适合的。2.Anticipatory scheduler(as) 曾经一度是 Linux 2.6 Kernel 的 IO scheduler 。Anticipatory 的中文含义是”预料的, 预想的”, 这个词的确揭示了这个算法的特点,简单的说,有个 IO 发生的时候,如果又有进程请求 IO *** 作,则将产生一个默认的 6 毫秒猜测时间,猜测下一个 进程请求 IO 是要干什么的。这对于随即读取会造成比较大的延时,对数据库应用很糟糕,而对于 Web Server 等则会表现的不错。这个算法也可以简单理解为面向低速磁盘的,因为那个”猜测”实际上的目的是为了减少磁头移动时间。
3.Completely Fair Queuing 虽然这世界上没有完全公平的事情,但是并不妨碍开源爱好者们设计一个完全公平的 IO 调度算法。Completely Fair Queuing (cfq, 完全公平队列) 在 2.6.18 取代了 Anticipatory scheduler 成为 Linux Kernel 默认的 IO scheduler 。cfq 对每个进程维护一个 IO 队列,各个进程发来的 IO 请求会被 cfq 以轮循方式处理。也就是对每一个 IO 请求都是公平的。这使得 cfq 很适合离散读的应用(eg: OLTP DB)。我所知道的企业级 Linux 发行版中,SuSE Linux 好像是最先默认用 cfq 的.
4.NOOP Noop 对于 IO 不那么 *** 心,对所有的 IO请求都用 FIFO 队列形式处理,默认认为 IO 不会存在性能问题。这也使得 CPU 也不用那么 *** 心。当然,对于复杂一点的应用类型,使用这个调度器,用户自己就会非常 *** 心。
Linux2.6版本的
Linux
内核使用了新的调度器算法,它是由
Ingo
Molnar开发的
O(1)调度器算法。它在高负载的情况下极其出色,并且对处理器调度有很好的扩展。
Linux2.4
版本的标准调度器中,使用时间片重算的算法。这种算法要求在所有的进程都用尽时间片以后,重新计算下一次运行的时间片。这样每次任务调度的花销不确定,可能因为计算比较复杂,产生较大调度延迟。特别是多处理器系统,可能由于调度的延迟,导致大部分处理器处于空闲
状态,影响系统性能。
新的调度器采用
O(1)的调度算法,通过优先级数组的数据结构来实现。优先级数组可以使每个优先级都有相应的任务队列,还有一个优先级位图,每个优先级对应位图中一位,通过位图可快速执行最高优先级任务。因优先级个数是固定的,所以查找的时间也固定,不受运行任务数的影响。
新的调度器为每个处理器维护
2
个优先级数组:有效数组和过期数组。有效数组内任务队列的进程都还有可以运行的时间片;过期数组内任务队列的进程都没有时间片可以执行。当一个进程的时间片用光时,就把它从有效数组移到过期数组,并且时间片也已经重新计算好了。当需要重新调度这些任务的时候,只要在有效数组和过期数组之间切换就好了。这种交换是O(1)算法的核心。
关于该算法的更多内容,google
一下!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)