互联网通信发展史其实是人类与 RTT 斗争的历史,详情请继续阅读下文。
没有多少基础的读者可以先阅读:
在浏览器地址栏输入一个 URL 后回车,背后会进行哪些技术步骤?
为了更好理解文章,需要解释一下什么是 RTT?
RTT是Round Trip Time的缩写,通俗地说,就是通信一来一回的时间。
TCP 建立连接时间
最早大家使用 TCP 来运输 HTTP,TCP 想必大家很熟悉了,需要三次握手,建立了 TCP 虚拟通道,那么这三次握手需要几个 RTT 时间呢?
一去 (SYN)
二回 (SYN+ACK)
三去 (ACK)
相当于一个半来回,故 TCP 连接的时间 = 1.5 RTT 。
HTTP 交易时间
这意味着,用户在浏览器里输入的网址 URL,直到时间流逝了 1.5RTT 之后,TCP 才开始运输 HTTP Request,浏览器收到服务器的 HTTP Response,又要等待的时间为:
一去(HTTP Request)
二回 (HTTP Responses)
故 HTTP 的交易时间 = 1 RTT
那么基于 TCP 传输的 HTTP 通信,一共花费的时间总和:
HTTP 通信时间总和= TCP 连接时间 + HTTP 交易时间 = 1.5 RTT + 1 RTT = 2.5 RTT
安全加密通信
随着互联网的爆发式增长,人类发现完全明文传输的 HTTP 通信很不安全。做为 OSI 七层参考模型的现实实现的 TCP/IP 协议,在设计之初没有考虑安全加密的环节。
互联网先驱Netscape公司,创造性发明了SSL(Secure Socket Layer),SSL 位于 TCP 与 HTTP 之间,做为 HTTP 的安全供应商,全权负责 HTTP 的安全加密工作。
IP / TCP / SSL / [HTTP]
各个通信模块之间的站位如上所示,将 HTTP 用[ ]括起来,表示 HTTP 被 SSL 安全加密了。
随着 SSL 的名气攀升,互联网标准化组织 IETF,觉得 SSL 是一个好东西,就拿来用了。
但 SSL 最初只是用于加密 HTTP 的,IETF 觉得这是一个硬伤,为什么不能用来做为所有应用层协议的安全供应商呢?来传输邮件、文件、新闻等等。实现这一点很简单,只要在协议里增加一个 Application Protocol 类型字段。
在 Application Protocol 有一个类型是“IP”, 意味着 TLS 不仅可以运输应用层协议如 HTTP、FTP,还可以运输 IP,这就是Cisco Any Connect的应用场景。
TLS (Transport Layer Security)
于是,IETF 在 SSL 3.0 版本的基础上,重新设计并命名了这个协议,其全新的名字为TLS,最初的版本为 1.0 版本。从其名字就可以看出,其核心使命就是保证传输层的安全。各个通信部门成员的占位与 SSL 占位一致:
IP / TCP / TLS / [HTTP]
到目前为止,浏览器支持的 TLS 版本为 TLS 1.0、1.1、1.2,当然版本越高越成熟、越安全。
HTTPS
通常将 TLS 安全保护的 HTTP 通信,称之为 HTTPS,以区别于没有 TLS 安全防护的 HTTP 明文通信。
交待了上文的背景知识,还是要回到本文的主题,来看看自从引入了 TLS 安全防护,看看 HTTPS 通信的 RTT 增加到了多少?
TLS 1.2
以 1.2 版本为例,看看 HTTPS 通信一共要消耗几个 RTT 时间?
1. 浏览器给服务器发送的 Client Hello 消息(一去)
首长好,我支持 1.2 版本,加密套件列表 1、2、3…,以及我的随机码 N1,请出示您的证件。
2. 服务器给浏览器发送的 Server Hello 消息(二回)
同志们好,那就 1.2 版本通信吧,加密套件我选用 1,我的随机码 N2,ECDHE 密钥交换素材 2,这是我的证件。
同志们辛苦了!
3. 浏览器给服务器发送的 Key Exchange 消息(三去)
为人民服务! 嘴里虽这么说着,私下还要偷偷验证首长的证件是否伪造的。
首长证书验证成功之后,还要给首长会话呢?会话内容如下:
首长辛苦了! 我的 ECDHE 密钥交换素材 1,接下来我发给您的消息都要加密了(Change Spec)。
从这以后,双方的 HTTP 通信将使用 TLS 加密了。一共花费了 1.5 个 RTT 时间。
让我们来计算一下整个 HTTPS 通信花费的时间总和:
HTTPS 通信时间总和 = TCP 连接时间 + TLS 连接时间 + HTTP 交易时间 = 1.5 RTT + 1.5 RTT + 1 RTT = 4 RTT
如果浏览器与服务器物理距离很近,RTT < 10 ms,即使 4 RTT 最大也不过 40 ms 的时间,用户压根感觉不到慢。
如果浏览器与服务器相隔上万公里,一个 RTT 时间通常在 200ms 以上,4RTT 时间通常在 1 秒以上,用户会明显感觉到网速慢了。
HTTP 1.x
和很多人想象不一样的是,浏览器从服务器获取的一个页面,通常由很多资源链接所组成。
服务器给浏览器推送的第一个页面,页面里通常嵌入了图片资源文本链接、以及动态页面资源链接、或第三方网站的链接资源,还需要浏览器根据这些文本链接内容,去链接所对应的服务器,继续下载链接所对应的内容。
浏览器通常采用的流程是,重新建立一个 TCP 连接、TLS 连接、HTTP 交易。
这又是一个漫长的 4RTT 等待过程,用户看到浏览器完整页面的时间为
完整页面加载时间 = 4RTT *2 = 8RTT
HTTP /2
自然有人会问,既然第一次页面与第二次页面都是同一个网站服务器,为何第二次页面要重新建立一个 TCP 连接,一个 TLS 连接?
如果重用第一个 TCP 连接,那么就少了 1.5 RTT + 1.5 RTT = 3 RTT 的时间。
这是一个好主意,就是用户的多个 HTTP Request 请求,使用同一个逻辑通道进行运输,这样会大大减少重新建立连接所花费的时间。
但是,这样会带来一个副作用,多个 HTTP 流使用同一个 TCP 连接,遵守同一个流量状态控制。只要第一个 HTTP 流遭遇到拥塞,剩下的 HTTP 流压根没法发出去,这就是头部阻塞(Head of line Blocking)。
既然 TCP 不好用,那为何要吊死在 TCP 这一棵树上呢?
外面的世界很精彩,到外面的世界逛逛。
QUIC(Quick UDP Internet Connection)
逛下来的感受是,UDP 不需要连接,不会带来附加的 RTT 时间,UDP 是一个好的合伙人被 HTTP /2 拉上了贼船,各合伙人的站位如下:
IP / UDP / QUIC
这个就是 Google 开发 QUIC 协议,QUIC 协议集成了 TCP 可靠传输机制、TLS 安全加密、HTTP /2 流量复用技术,其页面的加载时间为 2.5 RTT 时间。
此外,完成 QUIC 交易的连接的 Session ID 会缓存在浏览器内存里,如果用户再次打开该页面,无需建立 TLS 连接,直接使用缓存 Session ID 对应的加密参数,服务器可以根据 Session ID 在缓存里查找对应的加密参数,并完成加密。
换句话说,重连 TLS 连接是一个 0 RTT 事件,用户所要等待的页面加载事件 = HTTP 交易事件 = 1 RTT。
HTTP /3
这一次 IETF 又觉得 QUIC 是一个好东西,但是希望 QUIC 不仅可以运输 HTTP,还可以运输其它协议,把 QUIC 与 HTTP 分离,最终各合伙人的占位如下所示:
IP / UDP / QUIC / HTTP
这样整体的页面加载时间为 2 RTT。
TLS 1.3
IETF 的 QUIC 标准集成了 TLS 1.3 版本,1.3 版本更简练,建立 TLS 连接不再需要 1.5 RTT,而只需要 1 RTT,是因为浏览器第一次就把自己的密钥交换的素材发给服务器,这样就节省了第三次消息,少了 0.5 个 RTT 时间。
页面的整体加载时间 = TLS 1.3 连接时间 + HTTP 交易时间 = 1RTT + 1RTT = 2 RTT
重连页面的加载时间 = HTTP 交易时间 = 1 RTT
上文协议的进化过程就是人类与 RTT 斗争史,目标是减少用户等待页面加载时间、同时保证用户看到的页面安全,没有在传输过程中被偷窥、篡改。
HTTP /3 所带来的挑战
99%+ 以上的手机移动终端、电脑终端,都使用私有 IP,都需要 NAT 设备来完成私有 IP 与全球 IP 的转换。这意味着 NAT 设备通常会记忆用户的通信状态,一旦用户完成了通信,NAT 设备会释放这些记忆。
对于基于 TCP 的 HTTP、HTTPS 传输,NAT 设备可以根据 TCP 报文头的 SYN / FIN 状态位,知道通信什么时候开始,什么时候结束,对应记忆的开始、记忆的结束。
但是基于 UDP 传输的 HTTP/3,NAT 设备收到流量会知道连接什么时候开始,但是却无法知道流量什么时候结束。
NAT 设备的记忆如果短于用户会话时间,则用户会话会中断。
NAT 设备的记忆如果大大长于用户会话时间,则意味着 NAT 设备的端口资源会白白被占用!
最直接的解决方案是,在 QUIC 的头部模仿 TCP 的 SYN/FIN 状态,让沿途的 NAT 设备知道会话什么时候开始、什么时候结束。但这需要升级全球所有的 NAT 设备的软件!
另外一个可行的方案是,让 QUIC 周期性地发送Keepalive消息,刷新 NAT 设备的记忆,避免 NAT 设备释放自己的记忆。
最后留给读者一个思考题,为何 HTTP/3 不直接站在 IP 身上,而是站在 UDP 身上?