从 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

Made with Slides.com