TCP 和 QUIC

title

当前互联网几乎全部的 HTTP 通讯都由 TCP/IP 来承载,但 TCP 为了可靠性而牺牲性能被不少人所诟病。再到 QUIC 及 HTTP over QUIC 的发布,通过改进的 UDP 仿佛正在为将来替代 TCP 作准备。虽然 HTTP/3 尚未大面积普及,但愈来愈多的公司及我的也在不断尝试在传输层提高网络链接的效率。这篇文章主要讨论 TCP 及其问题和 QUIC 协议相关的内容。算法

关于 TCP 及其性能评估

传输层 TCP 协议为 HTTP 提供了一条可靠的比特传输管道。当 HTTP 传输一条报文时,会以流的形式将报文数据的内容经过一条打开的 TCP 链接按序输送。TCP 收到数据流后,会将数据流砍成数据块(段),并将其封装在 IP 分组中,数据格式以下图:服务器

DATA

传输协议在设计时须要对各类条件和场景进行策略的权衡取舍,TCP 为了可靠性设计了一系列的规则好比三次握手,四次挥手等等。下面咱们来展开讨论下 TCP 链接细节以及性能问题。网络

创建链接

TCP 慢启动拥塞控制

为了防止 TCP 链接一开始向对方发送大量数据而致使网络拥塞崩溃,TCP 链接通常会随着时间进行自我调谐,起初限制链接的最大速度,若是数据传输成功,则会提升传输速度,这种自我调谐被称为 TCP 慢启动,用于防止因特网的忽然过载和拥塞。并发

TCP 链接控制速度大小则经过拥塞控制窗口,在创建之初双方会协商传输的数据大小,而后发送方的拥塞控制窗口大小会跟随接收方的响应而变化,若是发送成功则线性增加,若是丢包则减半。负载均衡

三次握手

为何要三次握手

3-way

创建 TCP 链接时,须要通讯双方首先要对 Socket,序列号以及窗口大小信息达成共识,Socket 是由互联网地址标识符和端口组成,窗口大小用来作流控制,序列号则是用来追踪通讯发起方发送的数据包序号,接收方能够经过序号来向发送方确认某个数据包的成功接收。socket

历史链接判断tcp

咱们知道一个客户端是能够重复的与同一台服务器创建链接进行通讯的,RFC 793 中就指出三次握手的首要缘由就是阻止历史重复链接致使的混乱问题。ide

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.高并发

假如 TCP 链接为两次握手,那服务器方将只能选择接受或拒绝发送方屡次的请求,它并不清楚哪个是过时的链接。性能

为解决这个问题,TCP 选择了三次握手并在链接引入了 RST 这一控制消息,发送方在收到接收方的 ACK 确认号后,会判断是否为历史链接,若是是会发送 RST 控制消息停止此次链接。若是不是则会继续完成握手流程。

序列号控制

因为网络具备很大的不肯定性,可能会致使下面的问题:

  • 数据包被发送方屡次发送形成的数据重复
  • 数据包在传输过程当中被路由或者其余节点丢失
  • 数据包到达接收方可能没法按照发送顺序

因此 TCP 在数据包中加入了 SEQ 字段,有了数据包的序列号则能够:

  • 接收方能够经过序列号对重复的数据包去重
  • 发送方会在对应数据包未被 ACK 时重复发送
  • 接收方能够根据数据包的序列号对它们进行重排

避免数据丢失形成的循环

咱们再假设另一个场景,发送方发送了一个链接请求分组,对方收到后并发送确认应答分组,若是这时候认定链接创建,接收方会开始给发送方传输数据分组,但在应答分组丢失的状况下发送方不知道是否创建好链接,将会忽略任何数据分组直到应答分组收到为止。而接收方在发出的分组超时后会重复发送,这也造成了某种意义的死循环。

性能损耗

三次握手须要双方一共发送 3 个 TCP 分组(一般是 40 ~ 60)个字节,那么三次则是 120 字节到 180 字节,但大多数 HTTP 请求都不会携带大量的数据,小的 HTTP 事务极可能会在 TCP 创建上花费 50% 或者更多时间,同时也会增长不少字节的额外开销。

延迟确认与重传机制

因为咱们没法确保发送的数据分组必定能被对方收到,因此 TCP 实现了本身的确认机制来确保数据的成功传输。

每一个 TCP 段都有一个序列号和数据完整性校验和 (checksum),接收者收到无缺的段时都会向发送者回送小的确认分组。若是发送者没有在指定窗口时间(一般有 100 ~ 200 ms)内收到确认信息时,发送者就认为分组已被破坏或损毁,并重发数据。

ACK 的方式很容易保证消息的顺序性,但在某些状况下会致使发送方重传已经接收的数据:好比发送方发送四个数据段,后三个传输成功但第一个失败了,因为 ACK 语义是当前数据段前的所有数据段都已经被接收和处理,因此接收方没法发送 ACK 消息,发送方看没有 ACK,全部数据段对应的计时器就会超时并从新传输数据。在丢包严重的网络下,这种重传机制会形成大量的带宽浪费。

断开链接

四次挥手

4-way

如图所示,TCP 链接的拆除须要通过 four-way handshake,客户端或服务器都可主动发起挥手动做。

为何创建链接是三次握手而断开时是四次呢?

创建链接时,服务器开始处于 LISTEN 状态,收到 SYN 报文后能够把 ACK 和 SYN 放在一个报文回送给客户端。 但关闭链接时,服务器收到对方的 FIN 报文时,仅仅表示对方再也不发数据了但还能接收数据,本身未必所有数据都已经发送给对方,因此此时能够即刻关闭,也能够发送一些数据后再发送 FIN 报文给对方关闭链接,因此通常 ACK 和 FIN 会分开发送。

TIME_WAIT

当 TCP 端点关闭 TCP 链接时,会在内存中维护一个小的控制块,用来记录最近所关闭链接的 IP 地址和端口号。这类信息只会维持一小段时间,一般是所估计的最大分段使用期的两倍 2MSL, 以确保这段时间内不会建立相同地址和端口号的新链接。另外 TIME_WAIT 的存在也是为了保证 TCP 双工可靠的终止,虽然四次挥手发送和协调完毕,但咱们必须假设网络是不可靠的,没法保证最后发送的 ACK 报文必定会被对方收到,所以对方处于 LAST_ACK 状态下的 socket 可能会由于超时没收到 ACK 报文而重发 FIN 报文,因此这个 TIME_WAIT 能够用来重发可能丢失的 ACK 报文。

TIME_WAIT 时间是动态可配的,因此须要咱们本身设定策略来选择 MSL 时间。

HTTP/2 的优化

HTTP/2 基于 Google 推行的 SPDY,专一于性能,最大的目标是用户和网站间只须要单个链接。因此增长了二进制分帧,多路复用等强大的功能。同时 HTTP/2 协议也是最大限度的兼容 HTTP/1.x 的,request 模型,scheme 并无发生变化,不识别 HTTP/2 的代理服务器也能够将请求降级为 HTTP/1.x.

HTTP/2 并无改变 HTTP/1.x 的语义,只是在应用层使用二进制分帧的方式传输。所以引入了新的通讯单位:帧,消息,流。HTTP/2 是一个完全的二进制协议,头信息和数据包都是二进制的,统称为“帧“。

http-2

帧的类型包括了 DATA 帧 (用来承载请求或相应的内容,包括 Pad Length, Data, Padding 等字段),HEADERS 帧 (用来承载 start line + header 的 HTTP Header 帧), PRIORITY 帧 (stream 流发送方指定了建议优先级),PING 帧(用做心跳检测及计算 RTT 往返时间), GOAWAY 帧 (用于启动链接关闭或发出严重错误信号) 等等。

分帧使得服务器单位时间接收到的请求数变多,能够提升并发数,也为多路复用提供了底层支持。

多路复用

多路复用就是在一个 TCP 链接中能够存在多个 stream 流。

connection

多个 stream 同时存在时是无序的,因此须要 streamID 来标识,stream ID 使用无符号的 31 位整数标识,客户端发起的流必须使用奇数编号,服务器发起的则必须使用偶数编号,流标识符零 (0x0) 用于发送控制消息,并不能创建新的 stream .

数据流在发送中的任意时刻,客户端和服务器均可以发送信号 (RST_STREAM 帧) 来取消这个数据流。

多路复用有效的解决多个连接时慢启动,重复 TCP 握手耗时的问题使得 TCP 效率更高。

HTTP/2 其余特性和存在的问题

HTTP/2 使用 HPACK 来开启头部压缩,另外客户端和服务器同时维护一张头信息表,全部字段会存入该表,生成索引号,一样字段再发送时只发送索引号便可。HTTP/1.1 平均响应头有 500 个字节左右,而 HTTP/2 平均只有 20 多个字节,只有之前的 4% 左右。

HTTP/2 还增长了服务端推送可让服务端主动把资源文件推送给客户端。

但 HTTP/2 依旧存在一些问题好比:

  • 建连延时

TCP 链接依旧须要三次握手,消耗 1.5 个 RTT,再加上 TLS 握手,时间耗费将会更长。

RTT(Round-Trip Time):往返时延。表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便当即发送确认),总共经历的时延。

  • 队头阻塞没有完全解决

HTTP/2 在丢包时,整个 TCP 都要等待重传,会阻塞该 TCP 链接中的全部请求。

  • 多路复用致使的服务器压力

多路复用没有限制同时请求数,可能会致使许多请求的短暂爆发,致使 QPS 暴增。

  • Timeout

网络带宽和服务器资源有限,多个并行的流会致使资源被稀释,而后出现超时状况。

QUIC

QUIC(Quick UDP Internet Connections)协议基于 UDP 协议,它比较出色的解决了队头阻塞的问题,HTTP/3 也名 HTTP over QUIC,集成了 QUIC,TLS1.3 等等。

http3

QUIC 优点

主要特色为:

  1. 改进的拥塞控制,可靠传输
  2. 快速握手
  3. 多路复用
  4. 链接迁移

改进的拥塞控制,可靠传输

  • 应用层面能实现不一样的拥塞控制算法

一个应用程序的不一样链接能支持配置不一样的拥塞控制,应用程序不须要停机和升级就能实现拥塞控制的变动,能够针对不一样业务,网络和不一样的 RTT 来使用不一样的拥塞控制算法

  • 单调递增的 Packet Number 代替了 TCP 的 seq

每一个 Packet Number 都严格递增且惟一,而 TCP 重传存在二义性,从新发送时数据包中标识符都不变。

  • 不容许丢弃确认过的 Packet

QUIC 中的 ACK 包含了与 TCP 中等价的信息,但 QUIC 不容许 ACK 确认过的 Packet 被丢弃。这样不只能够简化发送端与接收端的实现难度,还能够减小发送端的内存压力。

  • 前向纠错能力 FEC

FEC 中,QUIC 数据帧的数据混合原始数据和冗余数据,来确保不管到达接收端的 n 次传输内容是什么,接收端都可以恢复全部 n 个原始数据包。

  • 更多 ACK 块和增长 ACK Delay 时间

QUIC 能够同时提供 256 个 ACK Block,在重排序时相对于 TCP 更有弹性,在丢包率比较高的网络下能够提高网络的恢复速度,减小重传量。

同时在计算 RTT 时会把接收到包到发送 ACK 的这段时间(ACK Delay)计算进去。

  • 基于 stream 和 connection 级别的流量控制

QUIC 的流量控制相似 HTTP/2,在 Connection 和 Stream 级别提供了两种流量控制。

快速握手

因为 QUIC 基于 UDP,因此只需花费 0~1 个 RTT 就能够创建链接。

多路复用

QUIC 是为多路复用设计的,携带个别流的数据包丢失时,一般只会影响该流,QUIC 链接上的多个 stream 之间并无依赖,也不会有底层协议限制。

这也很大程度上缓解了队头阻塞的影响。

链接迁移

TCP 经过 ip, 端口号来肯定链接, 而 QUIC 则经过 ConnectionID (64 bit) 来区别不一样链接。主要 Connection ID 不变链接就不须要从新创建。

面临的问题

QUIC 对于弱网环境的优化是明显的,可是也因为各类缘由面临着一些问题:

NAT 设备端口记忆问题

对于基于 TCP 的 HTTP(S) 传输,NAT 设备能够根据 TCP 报文头的 SYN/FIN 状态来了解通讯开始结束状态,对应记忆 NAT 映射的开始和结束,可是 UDP 中不存在 SYN/FIN 状态位,若是 NAT 设备的记忆短于用户会话时间则用户会话会被中断。

NAT 设备禁用 UDP

在一些网络环境下(好比校园网),UDP 协议会被路由器等中间网络设备禁止(包括我国的运营商= =), 这时客户端会直接降级, 选择备选通道。

负载均衡

QUIC 客户端存在网络制式切换,就算是同一个移动机房,可能第一次业务请求会落到 A 服务器,后续再链接则落到 B 服务器,重复走握手流程。

更多 QUIC 的内容能够参照 chromium/quic 官方文档


以上为这篇文章所有内容,部分资料参考自:

也欢迎关注公众号: RannDev

wechat
相关文章
相关标签/搜索