# Outline
回顾HTTP的发展简史,理解HTTP在设计上的关键转变,以及每次转变的动机
- HTTP简史
- HTTP/1.1的主要特性和问题
- HTTP/2 的核心概念、主要特性
- HTTP/2 的升级与发现
- HTTP/2 的问题及展望
# HTTP简史
HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上最普遍采用的一种应用协议- 由欧洲核子研究委员会
CERN的英国工程师Tim Berners-Lee在1991年发明 - Tim Berners-Lee也是WWW的发明者
# HTTP简史
HTTP/0.9:只有一行的协议- 请求只有一行,包括
GET方法和要请求的文档的路径 - 响应是一个超文本文档,没有首部,也没有其他元数据,只有
HTML - 服务器与客户端之间的连接在每次请求之后都会关闭
- 请求只有一行,包括
HTTP/0.9的设计目标传递超文本文档
# HTTP简史
HTTP/0.9演示
|
|
# HTTP简史
- 1996年
HTTP工作组发布了RFC 1945,这就是HTTP/1.0 - 提供请求和响应的各种元数据
- 不局限于超文本的传输,响应可以是任何类型:
HTML文件、图片、音频等 - 支持内容协商、内容编码、字符集、认证、缓存等
- 从超文本到超媒体传输
# HTTP简史
HTTP/1.0演示
|
|
# HTTP简史
- 1997年1月定义
HTTP/1.1标准的RFC 2068发布 - 1999年6月
RFC 2616发布,取代了RFC 2068 - 性能优化
- 持久连接
- 除非明确告知,默认使用持久连接
- 分块编码传输
- 请求管道,支持并行请求处理(应用的非常有限)
- 增强的缓存机制
- 持久连接
# HTTP简史
HTTP/1.1演示
|
|
|
|
|
|
# HTTP简史
- Google在2009年发布了实验性协议
SPDY,主要目标是解决HTTP/1.1的性能限制 - Google工程师在09年11月分享了实验结果 A 2x Faster Web
So far we have only tested SPDY in lab conditions. The initial results are very encouraging: when we download the top 25 websites over simulated home network connections, we see a significant improvement in performance - pages loaded up to 55% faster.
- 2012年,
SPDY得到Chrome、Firefox和Opera的支持 HTTP-WG(HTTP Working Group)开始在SPDY的基础上制定官方标准
# HTTP简史
- 2015年正式发布
HTTP/2- 主要目标:改进传输性能,降低延迟,提高吞吐量
- 保持原有的高层协议语义不变
- 根据W3Techs的报告,截止2019年11月,全球已经有 42.1% 的网站开启了
HTTP/2
# HTTP简史
- Google在2012年设计开发了QUIC协议,让
HTTP不再基于TCP - 2018年底,
HTTP/3标准发布 HTTP/3协议业务逻辑变化不大,可以简单理解为HTTP/2+QUIC
# HTTP/1.1 持久连接

# HTTP/1.1 持久连接

# HTTP/1.1 持久连接
- 非持久
HTTP连接的固定时间成本- 至少两次网络往返: 握手、请求和响应
- 服务处理速度越快,固定延迟的影响就越大
- 持久连接避免
TCP连接时的三次握手,消除TCP的慢启动
# HTTP/1.1 管道
- 多次请求必须满足先进先出(FIFO)的顺序

# HTTP/1.1 管道
- 尽早发送请求,不被每次响应阻塞

# HTTP/1.1 管道
HTTP/1.1的局限性- 只能严格串行地返回响应,不允许一个连接上的多个响应交错到达
- 管道的问题
- 并行处理请求时,服务器必须缓冲管道中的响应,占用服务器资源
- 由于失败可能导致重复处理,非幂等的方法不能pipeline化
- 由于中间代理的兼容性,可能会破坏管道
- 管道的应用非常有限
#
HTTP/1.1 的协议开销
- 每个
HTTP请求都会携带500~800字节的header - 如果使用了
cookie,每个HTTP请求会增加几千字节的协议开销 HTTP header以纯文本形式发送,不会进行任何压缩- 某些时候
HTTP header开销会超过实际传输的数据一个数量级- 例如访问
RESTful API时返回JSON格式的响应
- 例如访问
#
HTTP/1.1性能优化建议
- 由于
HTTP/1.1不支持多路复用- 浏览器支持每个主机打开多个连接(例如Chrome是6个)
- 应用使用多域名,将资源分散到多个子域名
- 浏览器连接限制针对的是主机名,不是
IP地址
- 浏览器连接限制针对的是主机名,不是
- 缺点
- 消耗客户端和服务器资源
- 域名分区增加了额外的
DNS查询 - 避免不了
TCP的慢启动
#
HTTP/1.1性能优化建议
- 使用多域名分区

#
HTTP/1.1性能优化建议
- 减少请求次数
- 把多个
JavaScript或CSS组合为一个文件 - 把多张图片组合为一个更大的复合的图片
- inlining内联,将图片嵌入到
CSS或者HTML文件中,减少网络请求次数
- 把多个
增加应用的复杂度,导致缓存、更新等问题,只是权宜之计
#
HTTP/2 的目标
- 性能优化
- 支持请求与响应的多路复用
- 支持请求优先级和流量控制
- 支持服务器端推送
- 压缩
HTTP header降低协议开销
- HTTP的语义不变
HTTP方法、header、状态码、URI
#
HTTP/2 二进制分帧层
- 引入新的二进制分帧数据层
- 将传输的信息分割为消息和帧,并采用二进制格式的编码

#
HTTP/2 的核心概念
- 流(Stream)
- 已建立的连接上的双向字节流
- 该字节流可以携带一个或多个消息
- 消息(Message)
- 与请求/响应消息对应的一系列完整的数据帧
- 帧(Frame)
- 通信的最小单位
- 每个帧包含帧首部,标识出当前帧所属的流
#
HTTP/2 的核心概念

#
HTTP/2 的核心概念
- 所有
HTTP/2通信都在一个TCP连接上完成 流是连接中的一个虚拟信道,可以承载双向的消息- 一个连接可以承载任意数量的
流,每个流都有一个唯一的整数标识符(1、2…N) 消息是指逻辑上的HTTP消息,比如请求、响应等消息由一或多个帧组成,这些帧可以交错发送,然后根据每个帧首部的流标识符重新组装
#
HTTP/2请求与响应的多路复用
HTTP/1.x中,如果客户端想发送多个并行的请求,那么必须使用多个TCP连接HTTP/2中,客户端可以使用多个流发送请求,同时HTTP消息被分解为互不依赖的帧,交错传输,最后在另一端重新组装

#
HTTP/2 帧格式

- 详细说明请参考HTTP/2规范
#
HTTP/2 帧类型
- 客户端通过
HEADERS帧来发起新的流 - 服务器通过
PUSH_PROMISE帧来发起推送流
| 帧类型 | 类型编码 | 用途 |
|---|---|---|
| DATA | 0x0 | 传输HTTP消息体 |
| HEADERS | 0x1 | 传输HTTP头部 |
| PRIORITY | 0x2 | 指定流的优先级 |
| RST_STREAM | 0x3 | 通知流的非正常 |
| SETTINGS | 0x4 | 修改连接或者流的配置 |
| PUSH_PROMISE | 0x5 | 服务端推送资源时的请求帧 |
| PING | 0x6 | 心跳检测,计算RTT往返时间 |
| GOAWAY | 0x7 | 优雅的终止连接,或者通知错误 |
| WINDOW_UPDATE | 0x8 | 针对流或者连接,实现流量控制 |
| CONTINUATION | 0x9 | 传递较大HTTP头部时的持续帧 |
#
HTTP/2 请求优先级
HTTP/2允许每个流关联一个31bit的优先值0最高优先级2^31 -1最低优先级
- 浏览器会基于资源的类型、在页面中的位置等因素,决定请求的优先次序
- 服务器可以根据流的优先级,控制资源分配,优先将高优先级的帧发送给客户端
HTTP/2没有规定具体的优先级算法
#
HTTP/2 流量控制
- 流量控制有方向性,即接收方可能根据自己的情况为每个
流,乃至整个连接设置任意窗口大小 - 连接建立后,客户端与服务器交换
SETTINGS帧,设置 双向的流量控制窗口大小 - 流量控制窗口大小通过
WINDOW_UPDATE帧更新 HTTP/2流量控制和TCP流量控制的机制相同,但TCP流量控制不能对同一个连接内的多个流实施差异化策略
#
HTTP/2 服务器端推送
- 服务器可以对一个客户端请求发送多个响应
- 服务器通过发送
PUSH_PROMISE帧来发起推送流 - 客户端可以使用
HTTP header向服务器发送信号,列出它希望推送的资源 - 服务器可以智能分析客户端的需求,自动推送关键资源

#
HTTP header压缩
HTTP/2使用HPACK压缩格式压缩请求/响应头- 通过静态霍夫曼码对发送的
header字段进行编码,减小了它们的传输大小 - 客户端和服务器使用
索引表来维护和更新header字段。对于相同的数据,不再重复发送
- 通过静态霍夫曼码对发送的
#
HTTP header压缩

#
HTTP/2 vs HTTP/1.1
#
HTTP/2的升级与发现
HTTP/1.x还将长期存在,客户端和服务器必须同时支持1.x和2.0- 客户端和服务器在开始交换数据前,必须发现和协商使用哪个版本的协议进行通信
HTTP/2定义了两种协商机制- 通过安全连接
TLS和ALPN进行协商 - 基于
TCP连接的协商机制
- 通过安全连接
#
HTTP/2的升级与发现
HTTP/2标准不要求必须基于TLS,但浏览器要求必须基于TLS- Web上存在大量的代理和中间设备:缓存服务器、安全网关、加速器等等
- 如果任何中间设备不支持,连接都不会成功
- 建立
TLS信道,端到端加密传输,绕过中间代理,实现可靠的部署 - 新协议一般都要依赖于建立
TLS信道,例如WebSocket、SPDY
#
h2和h2c升级协商机制
- 基于
TLS运行的HTTP/2被称为h2 - 直接在
TCP之上运行的HTTP/2被称为h2c

#
h2c演示环境
- 客户端测试工具
curl(> 7.46.0) - 服务器端
Tomcat 9.x
|
|
#
h2c协议升级
curl http://localhost:8080 --http2 -v
|
|
#
HTTP/2连接建立

#
HTTP/2连接建立
- Magic帧
- ASCII 编码,12字节
- 何时发送?
- 接收到服务器发送来的 101 Switching Protocols后
- TLS 握手成功后
- Preface 内容

#
HTTP/2连接建立
- 交换
settings帧(client -> server)

#
HTTP/2连接建立
- 交换
settings帧(server -> client)

#
HTTP/2连接建立
settingsACK 帧 (client <-> server)

#
TLS协议的设计目标
- 保密性
- 完整性
- 身份验证
#
TLS发展史
- 1994年,NetScape 设计了
SSL协议(Secure Sockets Layer) 1.0,未正式发布 - 1995年,NetScape 发布
SSL2.0 - 1996年,发布
SSL3.0 - 1999年,IETF标准化了
SSL协议,更名为TLS(Transport Layer Security),发布TLS1.0 - 2006年4月,IETF 工作组发布了
TLS1.1 - 2008年8月,IETF 工作组发布了
TLS1.2 - 2018年8月,
TLS1.3正式发布
#
TLS 1.2 握手过程

- 验证身份
- 达成安全套件共识
- 传递密钥
- 加密通讯
非对称加密只在建立
TLS信道时使用,之后的通信使用握手时生成的共享密钥加密
#
TLS 安全密码套件

- 密钥交换算法
- 双方在完全没有对方任何预先信息,通过不安全信道创建密钥
- 1976年,Diffie–Hellman key exchange,简称 DH
- 基于椭圆曲线(Elliptic Curve)升级DH协议,ECDHE
- 身份验证算法
- 非对称加密算法,Public Key Infrastructure(PKI)
- 对称加密算法、强度、工作模式
- 工作模式:将明文分成多个等长的
Block模块,对每个模块分别加解密
- 工作模式:将明文分成多个等长的
- hash签名算法
#
TLS1.3的握手优化

#
测试TLS的支持情况

# Application-Layer Protocol Negotiation
-
基于
TLS运行的HTTP/2使用ALPN扩展做协议协商- 客户端在
ClientHello消息中增加ProtocolNameList字段,包含自己支持的应用协议 - 服务器检查
ProtocolNameList字段,在ServerHello消息中以ProtocolName字段返回选中的协议
- 客户端在
-
在
TLS握手的同时协商应用协议,省掉了HTTP的Upgrade机制所需的额外往返时间
# ALPN

#
h2演示环境
- 客户端:浏览器
- 服务器端:
Tomcat 9.xTomcat提供了三种不同的TLS实现- Java运行时提供的
JSSE实现 - 使用了
OpenSSL的JSSE实现 APR实现,默认情况下使用OpenSSL引擎
- Java运行时提供的
#
Tomcat三种TLS实现
- JSSE
- 非常慢
- ALPN是因为
HTTP/2才在2014年出现,JDK8不支持ALPN
OpenSSL实现- 只使用了
OpenSSL,没有使用其他本地代码(native socket, poller等) - 可以配合 NIO 和 NIO2
- 只使用了
APR- 大量的native code
TLS同样使用了OpenSSL
#
TLS实现的性能对比
OpenSSL性能比纯Java实现好很多;使用TLS可以不再需要APRLinux上NIO.2是通过epoll来模拟实现的EPollPort.java

#
使用JSSE
- 生成private key和自签名证书
keytool -genkey -alias tomcat -keyalg RSA
- 配置
server.xml
|
|
#
使用JSSE
JDK8不支持ALPN
|
|
JDK11
|
|
#
使用OpenSSL
- 安装
tomcat-nativebrew install tomcat-native
- 配置
$CATALINA_HOME/bin/setenv.sh
|
|
- 配置server.xml
|
|
#
使用OpenSSL
JDK8&JDK11
|
|
#
ALPN协议协商
- ClientHello

#
ALPN协议协商

- ServerHello
# 使用Chrome开发者工具观察

# HTTP/2的问题
HTTP/2消除了HTTP协议的队首阻塞现象,但TCP层面上仍然存在队首阻塞HTTP/2多请求复用一个TCP连接,丢包可能会block住所有的HTTP请求

# HTTP/2的问题
TCP及TCP+TLS建立连接需要多次round trips
# QUIC
- Quick UDP Internet Connections
- 由Goolge开发,并已经在Google部署使用

# QUIC
QUIC: next generation multiplexed transport over UDP

