加快网络速度-TCP优化

现在, 时间就是金钱. 不像以前浏览一个网页就是一个奢侈品. 如今, 网速越来越快, 下个2GB的东西, 1分钟就好了.
那, 我现在网速很慢,应该怎么提高的我的网速呢?
提升网速的不二法门就是… 买宽带~
233333~ 当然,这只是, 给用户的建议。 对于, 我们程序员来说, 花钱就是最痛苦的事. 这里, 我们需要用我们自己的双手去提升网速.
这里,我们主要看看,在TCP这边怎么提升网络速度.

常见的TCP 延时事务

TCP是网络通信很重要的一个协议,程序员的基本功也就在这, 最出名的应该算是TCP的3次握手了. 不过,这里3次握手不是我要阐述的, 有兴趣的同学,可以看看TCP3次握手. 那还有其他能造成TCP时延的事务吗?
当然有啊. 常见能够造成的事务有以下几种.

  • 延迟确认

  • nagle算法

  • slow-start

  • 端口耗尽

延迟确认

当你在进行网络数据传输, 成功发送数据包时, 服务器会给你返回一个ACK进行确认。 但是为了防止,网络的堵塞,通常,服务器在接收该数据时,会对ACK进行延时, 如果在一定时间内(通常为200ms), 有另外的数据来源时, 则会将2次ACK包一起发送,减少宽带.
如下图所示:
《加快网络速度-TCP优化》
总结一句话:

ACK every second packet, or a single packet after the Delayed ACK timer expires

即,有两个包立即到来立即发送ACK, 没有的话,等一会,实在没有则将该次ACK 单独发送.

nagle算法延时

这是nagle 创建的一个算法,和延迟确认一样也是用来解决网络堵塞问题. 不过,他针对的是Sender一端. 众所周知的TCP的大小有40 bytes. 如果你的数据包内容才1B的话, 这样传输的价值就非常小了.所以,聪明的nagle想了想,这样不行,得让小数据包在缓存里面待一段时间,等数据多了再一起发送,如果是在没有其他数据了, 超过nagle算法设置的时延后,那就只能单独发送了.

nagle 和 确认延迟共同作用

假设现在有这样一个场景, 有两个包, 一个已经发送,另外一个由于尺寸太小, 被放在缓存当中. 此时,你的nagle算法是开启的, 那么此时,你需要等到前面一个包被确认之后才能发送.那么这样算起来,在这个特殊情况下,你偶数包的延迟时间是=== nagle+确认延迟. 虽然,这种情况很特殊,但是在高并发的情况下,任何东西都是有可能出现的.

nagle的缺陷

由于nagel主要是针对小数据包, 所以对于小包的影响很大. 比如: 你使用HTTP只是发送GET请求,要求数据库进行相关的取操作,而返回的数据很小. 那么,这时候nagle会让你欲哭无泪~

解决缺陷

针对于确认时延, 这里由于是系统自己设置的.
在windows 里,确认延迟一般为200ms.(现在谁还用windows)
而在*nx里,延时被调整到15-40ms.
不过在linux里面可以使用tcp_delack_min进行修改. 不过对于15-40ms, 俺认为这个应该没有太大的影响. 没必要大动干戈进行改动。 不过,对于nagle算法延时,这个问题就比较严肃了. 在nodeJS里面我们一般使用socket.setNoDelay([noDelay])来进行设置.

slow start为何物

slow-start 是为了解决网络延迟而被设计出来的一个算法. 用来逐步增加数据包的发送量,直到发送端和接收端能够承载的最大值为止.
在正式介绍slow-start 工作流程之前,我们需要掌握几个基本的概念。

TCP 里面的window

window是TCP报文里面的一部分. 我们看一下TCP协议内容.
《加快网络速度-TCP优化》
就是里面的window. 他主要就是用来描述 连接能够承受的包的大小, 因为一旦你的包过大,会造成包的废弃,而导致重发。window 就是起到提高传输效率的作用.
那window 是怎么确定的呢? 首先我们需要明白, 每一方的 congestion window 的最大值,都只有自己知道,那要猜测到对方的最大值话,就只能一步一步的逼近. 但第一次的值应该怎么设置呢? 其实这是有标准的.
首先,我们需要了解另外一个名词–MSS][4. 他是congestion window 的基础值. MSS 通常为1460B(1500-20 for IP – 20 for TCP). 然后,congest control 定义了一套公式:

If SMSS > 2190 bytes:

IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
If SMSS <= 1095 bytes:
IW = 4 * SMSS bytes and MUST NOT be more than 4 segments

所以由上面算下来, window = MSS * 3 = 4380B. 这就是sender’s congestion window 的初始值. 这里的3还有另外一个名词–initcwnd(下文会有介绍).
ok~ 基本概念有了, 那 how does slow-start work?

slow-start 是怎么工作的?

  • 在首次发送信息时, Sender 会初始化一个包, 并且该包里面包含了一个比较小的 堵塞窗口(其实就是表明窗口的容载量–bytes). 他的大小是由 MSS 决定的.比如,我们的MSS为1460B, 那么我们初始化的congeston window就可以为2920B. 即,相当于发送了2个小分组. (不理解,见window size)

  • 接受者接受到包之后, 会返回一个ACK包,并附上 receiver’s window size(通常,会比sender发送过来的大). 如果, receiver 没有响应, 那么sender 就知道自己 的包太大了, 然后会自行改小 然后再发送.

  • sender接受到receiver 的 ACK后,此时就在上一次发送的基础上额外加上一倍的MSS. 即, 上一次,我发送了2个MSS大小的包, 那么此时,会接受到两次ACK确认,接收到之后,我就可以增大window size, 向receiver额外再发送一倍—4个MSS大小(2920 + 1460 + 1460). 如此往复,直到两边的window size 到达上限之后,那么slow start 就已经完成了.

其实,上面的传输过程,我们可以使用一个图表来表示. 这是,在下载一个文件时抓包时的图.(抓包工具你用什么都可以,不过最常用的是fiddle和wireShark)
《加快网络速度-TCP优化》

x-axis 是 时间, y-axis 是 发送的序列号(用来表示每个包是整个资源的哪一部分). 注意!!! 里面的一个点 就是一个包. 我们可以数一数, 第一个有2个dot, 第二个有4个dot, 第三个有8个dot… 后面肯定会有一个上限值, 那么,此时就已经达到最大传输速度了. ok~ slow-start要做的工作就已经完成了。 那么该次的Connection 应该算是最优connection, 所以对于已经建立好的connection实现重用也是TCP优化很重要的一部分.

slow start 好处

  • 减少连接断开次数: 因为包不会由于网络堵塞而丢失

  • 用户能够享受更快的下载速度,因为此时slow-start已经找到了最大的连接速度了

  • 降低网络堵塞情况

但是,好处就这几个,但是对于大量连接需要建立时, slow-start 对其影响 就比较呵呵了。 那应该如果优化slow-start呢?

解决slow-start时延

上面提到过,congestion control 有一个计算机制, 用来确定初始化时的包的传输量.

If SMSS > 2190 bytes:

IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
If SMSS <= 1095 bytes:
IW = 4 * SMSS bytes and MUST NOT be more than 4 segments

上面的数字,我们就可以称作为initcwnd(the initial congestion window parameter). 他用来规定你的sender 在初始化时,发送数据包的数量. 由于通常规定MSS 一般为1460B, 所以,initcwnd 的数量一般规定为2.(因为,要比上限值小才行, 防止丢包)
但是,这个只是对于很老的机器来说, 现在大家的笔记本基本上都是00后, 配置已经远远超过以前的台式了. 所以,一般而言,receiver window size 应该不会很低。 下图是操作系统和对应的window size的最大值.
《加快网络速度-TCP优化》
就算是XP 他也可以接受 65 535B 大小的包. 如果你sender发送的还是 2920B 的话,这就有点 太慢了. 所以,在配置服务器的时候(特别是*nx), 我们需要对其initcwnd的值,更改一下. 减少他slow-start的时间.
在linux 下, 我们可以使用:

  • ip route show; //查看电脑设置的initcwnd初始值

  • sudo ip route change default via 192.168.1.1 dev eth0 initcwnd 10 //将initcwnd 设置为10

  • ip route show; //检查initcwnd 是否为10

在windows 2008 Server 下的更改请参靠: initcwnd

提升了initcwnd,有什么用?

那这样做到底有什么效果呢?我们看图说话:
《加快网络速度-TCP优化》
可以看到,随着initcwnd的提高, 下载的时间逐渐变慢. 但是,initcwnd 也不能无限制提高, 可以看到从10到20, 时间就基本上没什么变化了. 所以,一般推荐提升到10就可以了.

另一方面,initcwnd对于CDN 的优化,也是非常重要的。 要知道CDN 本来就是以快著名. 我们先来看看CDN 是如何工作的.

HOW CDN works

CDN凭借他特有的机制,高速通道,CDN Cache,域名分片等特性. 成为了云平台上必不可少的一个特性.
但,CDN到底是怎么工作的呢?
CDN是云平台的产物,所以他必须依赖的就是众多的服务器. 通常来说, CDN 有很多 Points of Presence (PoPs) 分布在全国或者说全世界各地,用来加速文件传输的.
如果,我们的文件没有放在CDN上,那么,他的传输过程是怎样的呢?
《加快网络速度-TCP优化》
像这里,从Russia 到 America中间经过无数的节点进行传输,可想而知,随随便便丢个包,那么, 你传输就得重头再来~ 造成的网络堵塞我就不说了,关键用户等不了~ 对于这种情况,考虑直接上CDN。 我们来看一下,带上CDN的时候.
《加快网络速度-TCP优化》
当你的文件放在CDN上时, 用户首次使用该文件时, 这时候最近的CDN Server 会向 Origin Server 请求文件,然后该文件就会保存在该CDN Server 以备用户下一次读取. 所以, 一般而言,第一个吃螃蟹的人,贡献是最大的.
另外,如果你请求的资源是动态生成的,那么CDN Cache你的Content也是没有什么卵用的. 所以CDN 还有另外一个机制– Super Highway. 不同于前面的without CDN 时候的传输方式, 经过独立的ISP, 用户等到花儿都谢了资源才到, 这里, CDN 会建立一个高速通道, 将动态资源高速传递.
《加快网络速度-TCP优化》
云平台内部会自建算法,计算两点位置传输的最短路径, 然后找到对应服务器进行传输. 所以,远距离传输也是CDN的一大亮点。 不过由于距离长,稳定性和安全性的要求也会增加. CDN服务器就必须保证 自己 本身能够过滤掉一些DDOS attackers 以及 能够承受部分的DDOS攻击.

ok~ CDN 基本内容算是说完了, 通过上面的介绍,为了达到’高速’这个蜜汁weapon. 提升initcwnd 也是必不可少的一部分。 国外的CDN,通常会到10+. 这是linux kernel 内置的(3.0版本以上). 可以说, 你的initcwnd 越高, 对机器的性能要求也越高。 所以,对于自己的云平台有蜜汁自信的,他的initcwnd肯定会高。 这里,我放一份,国外的CDN 服务商的 initcwnd数.
《加快网络速度-TCP优化》
可以看到. Cachefy 像是开挂似的, initcwnd数都到70了. 不过,initcwnd只是作为一个参考,服务商性能的优劣并不仅仅只有initcwnd这一个参数.

端口耗尽和TIME_WAIT

还记得4次挥手的时候,发生的故事么?
在双方发送一次FIN包之后,还需要经过TIME_WAIT的2MSL时延之后,才可以正式宣告结束.(MSL是报文在网络中存活的最大时间- Maximum Segment Lifetime)具体的4次回收流程如下:
《加快网络速度-TCP优化》
只有当TIME_WAIT正式结束之后, 端口的利用才有可能被释放. 所以,TIME_WAIT的2MSL 也可算是一个时延.
这里,我们需要了解一下,在TCP通信中,很重要的一个概念就是连接唯一性. 确认唯一性一般只要4个值即可.

  • client IP Address

  • client port

  • server IP Address

  • server port

这也是TCP报文和IP报文里面必不可少的部分。
所以,由于2MSL时延的关系,会造成client的端口堵塞。有可能会造成端口耗尽的结果。 那对于server 有什么影响呢?
在普通HTTP 请求当中对于server的影响可以说没有,但如果你使用的是socket通信的话,那么影响就比较大了, 因为在断开的时候,server 的socket 就会处于TIME_WAIT状态, 有可能造成服务器的端口耗尽,在nodeJS里面可以使用setTimeout()进行 自动断开等优化操作.
但是, 为什么客户端一定要有2MSL的延时呢?
很简单,就是保证数据的正确性.
这里,我们可以这么考虑, 如果没有2MSL, TCP连接的数据有可能发生神马情况?
如图:
《加快网络速度-TCP优化》
point_1 向point_2发送信息时,有一个包,过度延时(在2MSL内). 但此时,point_2向point_1发送断开请求,没有时延的情况下, 两者会立即断开.然后接着, point_1又向point_2 建立3次握手连接。 完成之后,延迟的包又发送过来,由于包的IP和port都是正确的,point_2当然会无条件处理,而结果就是,将现存的数据给改变–有可能造成数据bug.
所以,2MSL的时间很有必要.
但,由于2MSL的必要性,会对于某些大并发操作的连接产生巨大影响, 比如使用siege,ab进行基准测试, 大并发查询数据库等等. 所以, 对于这次,实现连接的reuse就非常必要了.

    原文作者:TCP
    原文地址: https://segmentfault.com/a/1190000004670986
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞