从 HTTP0.9 到 QUIC
About me
- 孙宁
- https://sunng.info
- https://githun.com/sunng87
- LeanCloud
- https://leancloud.cn
HTTP
0.9
(1991)
telnet www.example.com 80
GET /index.html
<html>...
<EOF>
GET
当时唯一的 HTTP 方法
/index.html
HTML 文档的逻辑位置
CR LF
/r/n
请求结尾
<HTML>
服务器返回请求的文档
EOF
断开连接
表示文档返回完毕
HTTP 1.0
(1996)
telnet www.example.com 80
GET / HTTP/1.0
User-Agent: HappyBrowser
Accept: */*
HTTP/1.0 200 OK
Content-Type: text/html
Server: HappyServer
<html>....
<EOF>
HTTP/1.0
请求行增加了版本号
方便与未来版本区分
HEADERs
请求和响应
增加了 HTTP 头
传递扩展信息
200 OK
响应增加了状态码
表示响应的类型
1.0 的问题
每个请求都要创建新的 TCP 连接
响应完成后连接会被断开
然而创建 TCP 连接的代价很大
- TCP 连接创建的的握手机制
- TLS 的握手机制*
- TCP 的慢启动机制
TCP 握手
1 个 RTT
Client
Server
1-RTT
TCP Handshake
TLS 握手
至少 1 个 RTT
Client
Server
1-RTT
1-RTT
TLS handshake
TCP 慢启动
TCP 吞吐量受拥塞窗口限制
窗口根据网络状况逐步放大
创建连接的代价
- 创建连接本身会产生(不必要)的延迟
- 连接创建之初吞吐量较低
- 可能在没有达到最大吞吐量之前连接就已关闭
HTTP 1.1
(1999)
telnet www.example.com 80
GET / HTTP/1.0
User-Agent: HappyBrowser
Accept: */*
HTTP/1.0 200 OK
Content-Type: text/html
Server: HappyServer
Connection: Keep-Alive
<html>....
keep-alive
HTTP 1.1 默认保持连接
telnet www.example.com 80
GET / HTTP/1.0
User-Agent: HappyBrowser
Accept: */*
Connection: Close
HTTP/1.0 200 OK
Content-Type: text/html
Server: HappyServer
Connection: Close
<html>....
<EOF>
👍💪👏
持久连接避免了反复创建连接的代价
HTTP 1.1 一直沿用至今
HTTP 1.1 - 2014
文档里的 HTTP
工作中的 HTTP
请求响应模式的
HTTP 连接在同一时刻
只能处理一个请求
Client
Server
HTTP
GET /something
200 OK
😐
然而页面有很多元素
连接池
每个域名打开6个连接
network.http.max-persistent-connections-per-server
仍然在排队
域名切分
通过切分域名获得更大的并行下载数
新建连接的影响更大了
只是(可能)还小于排队的代价
导致 74% 的HTTP连接没有被重用
http://bitsup.blogspot.com/2015/02/http2-is-live-in-firefox.html
实际上仍然会排队
HTTP Piplining
network.http.pipelining
Client
Server
HTTP
GET /a
200 OK
GET /b
200 OK
Pipelining 的缺点
- 仅支持幂等操作
- 要求顺序
- 要求顺序
- 要求顺序
- 要求顺序
- 要求顺序
Client
Server
HTTP
GET /a
?
GET /b
waiting
http://news.163.com/10/0820/07/6EH0HMVL00014AED.html
http://news.qq.com/a/20131211/010097.htm
HTTP/2
(2015)
SPDY
speedy
重点改进
- 二进制协议
- 多路复用(增加“流”概念)
- 头压缩
- Server Push
多路复用
在同一个 HTTP 连接上同时支持多组请求
解决应用层顺序问题
Client
Server
HTTP/2
GET /a (s1)
200 OK (s2)
GET /b (s2)
200 OK (s1)
多路复用就在身边
多路复用的好处
- 避免重复创建连接的代价
- 允许服务器端灵活的线程模型
- 全双工通信
6 个连接的连接池域名切分
HTTP/2 是否已经完美?
1. 创建连接的延迟仍然存在
TCP 握手,TLS 握手
TFO
TCP Fast Open
在客户端第一个 SYN 包中预传输数据
但 TFO 并未广泛推广
- Kernel 更新周期较慢
- 改变了客户端 API
TLS 1.3
0-RTT Handshake
在 Client Hello 和 Server Hello 中传输应用数据
2. 传输层的不兼容
HTTP/2 解决了应用层排队问题,但 TCP 本身要求顺序
Client
Server
HTTP/2 over TCP
GET /a (s1)
s2
丢包,等待重传
GET /b (s2)
s1
已经到达,但需要等待
TCP 不支持多路复用
- 丢包延迟
- 拥塞算法影响到所有流
QUIC
Quick UDP Internet Connection
QUIC
- Quick
- UDP
- Connection
Quick
- “流”概念成为传输层一等公民
- 拥塞控制,丢包重传都在“流”的层面
- 在“流”层面排队
- 在传输层面不再有排队
- 0-RTT 握手
- 高级的传输协议特性
- FEC(吞吐量换延迟)
- Negative ACK
- 网络切换 ConnectionID
- WiFi - 3G 切换无需重连!
Client
Server
HTTP/2 over QUIC
GET /a (s1)
s2
丢包,等待重传
GET /b (s2)
s1
已经到达,返回应用层
TCP
TLS
HTTP/2
HTTP/2 over TCP
UDP
QUIC
HTTP/2
HTTP/2 over QUIC
流管理
安全加密
拥塞控制,顺序
UDP
为什么使用 UDP
XHR/Fetch
浏览器
HTTP
Socket
TCP
操作系统
QUIC
libquic
UDP
UDP: Null protocol
UDP 是 IP 上的一层薄层
QUIC 将传输层移到用户空间
- 协议栈可以打包在 APP 里发布
- 可以更灵活的测试和发布算法
Connection
- “连接”并不物理存在,本身是一种抽象
- “连接”表示一种状态
QUIC 在 UDP 上实现了
TCP 的关键特性
- 可靠传输
- 顺序
- 重传
- 拥塞控制
- 流量控制
QUIC 在 TCP 功能上增加了
- 内置安全连接 TLS
- “流”抽象
在 Chromium 上体验 QUIC
chrome://flags/#enable-quic
Wrap up
延迟在不断降低
- HTTP 1.1 持久连接避免重建连接
- HTTP/2 多路复用避免创建多余连接
- QUIC, TFO, TLS 1.3 减少握手的往来
连接的利用率不断提高
- HTTP 1.1 持久连接在请求间重用 TCP 连接
- HTTP Pipelining 在多个请求时重用一个连接
- HTTP/2 通过多路复用重用一个连接
抽象和分层的兼容
- HTTP 的持久连接
- QUIC 的传输层“流”
FIN
从 HTTP 0.9 到 QUIC
By Ning Sun
从 HTTP 0.9 到 QUIC
介绍 HTTP 协议的过去、现在和未来发展方向,分析每一次的升级背后的动机和改善的要点。
- 1,370