HTTP 2.0

HTTP的历史

HTTP/0.9(1991)

请求示例:

telnet example.com 80
GET /
...
?

主要特性:

  • 仅支持GET方法
  • 响应类型仅超文本
  • 没有HTTP header
  • 连接在文档传输完毕后断开

备注:现在基本上大部分服务器已经不支持

HTTP/1.0(1996)

请求:

telnet example.com 80
GET / HTTP/1.0
User-Agent: HappyBrowser
Accept: */*

响应:

HTTP/1.0 200 OK
Content-Type: text/html
Server: HappyServer

It works

?

主要特性:

  • 增加了请求和响应header的支持(HTTP版本号、响应码和contentType)
  • 响应内容不在局限超文本(Content-Type用来标记其他资源的能力,如脚本、样式或媒体文件)
  • 增加了HEAD、POST方法的支持
  • 连接在响应传输完毕后依然会断开

HTTP/1.1 最常见的版本

http1.1 的标准最初是1999年提出,后来又不断有新的功能加入。
请求:

GET /sales/ HTTP/1.1
Host: www.mafengwo.cn
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://www.mafengwo.cn/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: mfw_uuid=5c74d16c-0900-dc1c-5d20-c579f2cbf02c; uva=s%3A78%3A%22a%3A3%3A%7Bs%3A2%3A%22lt%22%3Bi%3A1551160082%3Bs%3A10%3A%22last_refer%22%3Bs%3A6%3A%22direct%22%3Bs%3A5%3A%22rhost%22%3Bs%3A0%3A%22%22%3B%7D%22%3B; __mfwurd=a%3A3%3A%7Bs%3A6%3A%22f_time%22%3Bi%3A1551160082%3Bs%3A9%3A%22f_rdomain%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22f_host%22%3Bs%3A3%3A%22www%22%3B%7D; __mfwuuid=5c74d16c-0900-dc1c-5d20-c579f2cbf02c; UM_distinctid=16928588422186-0b9d939d8c4e34-36667105-13c680-169285884234f; mafengwo=9f8394af8a9966831e7983269da5caca_57022634_5c8b78a19ede19.06690157_5c8b78a19ede83.17999445; mfw_uid=57022634; PHPSESSID=folqrtiba2scaf4l5m80tn7bf4; __omc_chl=; __jsluid_h=844a8202521a956499032b60f37c890d; __mfwothchid=referrer%7Cwww.google.com; __mfwc=referrer%7Cwww.google.com; Hm_lvt_8288b2ed37e5bc9b4c9f7008798d2de0=1561357018,1561618439,1562290042,1563286896; oad_n=a%3A3%3A%7Bs%3A3%3A%22oid%22%3Bi%3A1029%3Bs%3A2%3A%22dm%22%3Bs%3A19%3A%22pagelet.mafengwo.cn%22%3Bs%3A2%3A%22ft%22%3Bs%3A19%3A%222019-07-16+22%3A21%3A36%22%3B%7D; __omc_r=; _r=mfwdev; _rp=a%3A2%3A%7Bs%3A1%3A%22p%22%3Bs%3A19%3A%22aos.mfwdev.com%2Fmain%22%3Bs%3A1%3A%22t%22%3Bi%3A1563347224%3B%7D; __jsl_clearance=1563627356.535|0|kk93xnOdc8BeDY9pWeEPqYZ6Hb0%3D; __mfwlv=1563627359; __mfwvn=230; CNZZDATA30065558=cnzz_eid%3D608222882-1551154850-%26ntime%3D1563627272; uol_throttle=57022634; __mfwa=1560765395471.11278.27.1563627359436.1563630649119; RT="dm=mafengwo.cn&si=4c7u8877fci&ss=1563630647923&sl=0&tt=0&obo=0&sh=&rl=1"; __mfwb=65bcbe0415ed.4.direct; __mfwlt=1563630878; Hm_lpvt_8288b2ed37e5bc9b4c9f7008798d2de0=1563630879

响应:

HTTP/1.1 200 OK
Date: Sat, 20 Jul 2019 13:55:38 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Vary: Accept-Encoding
X-Sid: 11.114
X-Pid: 589
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Encoding: gzip
X-Via-JSL: e68501a,-
X-Cache: bypass

主要特性:

  • 增加了虚拟主机的支持(一个IP支持多个域名,使用host关键字)、管道技术、默认启动Keep-Alive持久连接、传输编码功能
  • 支持方法增加PUT、DELETE、TRACE、OPTIONS

HTTP/1.X的困境

HTTP/1的连接
以下是HTTP/1的请求整个流程,一个TCP连接上同时只能有一个请求/响应。
HTTP 2.0_第1张图片

HTTP/1增加了Keep-Alive特性

未使用Keep-Alive功能的两次请求,无法复用TCP连接,每次请求必须重新建立和断开TCP连接,延迟相对高,性能较低。
HTTP 2.0_第2张图片
使用keep-alive的两次请求
HTTP 2.0_第3张图片
使用了keep-alive以后可以复用对应TCP连接,不需要频繁建立和断开TCP连接,降低了延迟。但是缺点是第二个请求必须等待第一次请求完成以后才能发送,这时候HTTP/1.1增加新特性管道来优化这块。

管道(Pipelining)

没有管道特性之前的两次请求
HTTP 2.0_第4张图片
使用管道特性发起两个请求示意图如下
HTTP 2.0_第5张图片
使用管道发出多个请求,不需要等待上个请求响应以后才能发起新的请求,有效提高了速度。但是也是有头阻塞问题,所谓头阻塞问题是指可以在不需要等待上个请求响应就可以发起新的请求,但是响应依然严格按照顺序返回,如果前一个响应很慢,会影响后边请求的响应到达。

重复的头部数据造成浪费

自HTTP/1.0以后,HTTP请求中通常带有大量以ASCII编码的头部,这些头部通常大部分都不会变化,每次请求都会携带,这给本就拥挤不堪的网络带来了更多的压力(尤其是像User Agent、Cookie这种值比较长的头部字段)。
下图是某网站的一个页面加载,这个页面有较多的图片,每个图片都带着对其没有用的cookie字段,造成极大的资源浪费。
HTTP 2.0_第6张图片

总结

  • 一个TCP连接上同时只能有一个请求/响应
  • HTTP 头部没有压缩,大量重复头部造成额外浪费

HTTP2.0 技术简介

HTTP 2.0 小试牛刀

https://http2.akamai.com/demo这是 Akamai 公司建立的一个官方的演示,用以说明 HTTP/2 相比于之前的 HTTP/1.1 在性能上的大幅度提升。 同时请求 379 张图片,从Load time 的对比可以看出 HTTP/2 在速度上的优势。

二进制分帧层

HTTP 2.0 并没有改动HTTP 1.X 的语义部分,列如请求方法、响应状态码、URL以及头部字段等核心概念依旧存在。HTTP 2.0最大的变化是重新定义了格式化和传输数据的方式,通过在高层HTTP API和底层TCP 连接之间引入二进制分帧层来实现。这样的好处是原来的WEB应用完全不用修改,就能享受协议升级带来的收益。
HTTP/1 的请求和响应报文,都是由起始行、首部和实体正文(可选)组成,各部分之间以文本换行符分隔。而 HTTP/2 将请求和响应数据分割为更小的帧,并对它们采用二进制编码。下面这幅图中的 Binary Framing 就是新增的二进制分帧层:
HTTP 2.0_第7张图片
相关概念:

  • 帧(Frame):HTTP/2 数据通信的最小单位。帧用来承载特定类型的数据,如 HTTP 首部、负荷;或者用来实现特定功能,例如打开、关闭流。每个帧都包含帧首部,其中会标识出当前帧所属的流;

  • 消息(Message):指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成;

  • 流(Stream):存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID. 每个http request都会新建自己的stream,response在同一个stream上返回。stream相当于HTTP 1.1中的请求。为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID。

    在 HTTP/2 中,同域名下所有通信都在单个连接上完成,这个连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。这也就是所谓HTTP 2.0的多路复用。下面有一幅图说明帧、消息、流和连接的关系:
    HTTP 2.0_第8张图片
    HTTP性能的关键在于低延迟而不是高带宽!大多数HTTP 连接的时间都很短,而且是突发性的,但TCP 只在长时间连接传输大块数据时效率才最高。HTTP 2.0 通过让所有数据流共用同一个连接,可以更有效地使用TCP 连接,让高带宽也能真正的服务于HTTP的性能提升。

同时,单链接多资源的方式,使到至上而下的层面都得到了好处:

  • 可以减少服务链接压力,内存占用少了,连接吞吐量大了

  • 由于 TCP 连接减少而使网络拥塞状况得以改观;

  • 慢启动时间减少,拥塞和丢包恢复速度更快。

服务器推送

HTTP 2.0 新增的另一个强大功能是服务器可以向客户端推送额外资源,HTTP1.x时严格遵循请求响应模式,先有请求然后有对应的响应;HTTP 2.0则可以主动向客户端推送资源,这种方式可以极大提升体验效果,提高页面资源的加载速度。
如下图,对应页面红颜色圈中的部分开启了push推送功能,相关资源加载极为迅速,优先级也很高。
HTTP 2.0_第9张图片
如下图所示,客户端请求stream 1,/page.html。服务端在返回stream 1消息的同时推送了stream 2(/script.js)和stream 4(/style.css)。
HTTP 2.0_第10张图片

头部压缩

每个HTTP1传输都会有状态行和头部数据,随着单个页面产生的请求数越来越多消耗在头部的流量越来越多,尤其UserAgent、Cookie这类不会频繁变动的内容,完全是一种浪费。HTTP 2.0 增加了头部压缩功能来解决这个问题
效果图1,这是进入网页首个请求header中user-agent占用的长度数据:
HTTP 2.0_第11张图片
这是第二个请求header中user-agent占用的长度数据:
HTTP 2.0_第12张图片
对比后可以发现,第二次的请求头部之所以非常小,是因为大部分键值对只占用了一个字节。尤其是 UserAgent、Cookie 这样的头部,首次请求中需要占用很多字节,后续请求中都只需要一个字节。
实现原理:
HTTP 2.0_第13张图片
头部压缩需要在支持 HTTP/2 的浏览器和服务端之间:

  • 维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;
  • 维护一份相同的动态字典(Dynamic Table),可以动态地添加内容;
  • 支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding);
    静态字典的作用有两个:1)对于完全匹配的头部键值对,例如 :method: GET,可以直接使用一个字符表示;2)对于头部名称可以匹配的键值对,例如 cookie: xxxxxxx,可以将名称使用一个字符表示。HTTP/2 中的静态字典(部分)如下:
    HTTP 2.0_第14张图片

同时,浏览器可以告知服务端,将 cookie: xxxxxxx 添加到动态字典中,这样后续整个键值对就可以使用一个字符表示了。类似的,服务端也可以更新对方的动态字典。需要注意的是,动态字典上下文有关,需要为每个 HTTP/2 连接维护不同的字典。

使用字典可以极大地提升压缩效果,其中静态字典在首次请求中就可以使用。对于静态、动态字典中不存在的内容,还可以使用哈夫曼编码来减小体积。

这里顺便说一下,HTTP/1 的状态行信息(Method、Path、Status 等),在 HTTP/2 中被拆成键值对放入头部(冒号开头的那些),同样可以享受到字典和哈夫曼压缩。另外,HTTP/2 中所有头部名称必须小写。

HTTP/2 协议协商

Upgrade 机制升级

支持HTTP 2.0的浏览器可以在网页请求最开始的时候自发完成和服务端的协议协商以确定是否使用HTTP 2.0通信。实现原理使用HTTP/1.1新增的Upgrade机制,Upgrade机制使用下面两个header关键字进行协议升级:

Connection: Upgrade
Upgrade: protocol-name[/protocol-version]

客户端通过 Upgrade 头部字段列出所希望升级到的协议和版本,设置 Connection 头的值为 “Upgrade” 来指示这是一个升级请求.
如果服务端不同意升级或者不支持 Upgrade 所列出的协议,直接忽略即可(当成 HTTP/1.1 请求,以 HTTP/1.1 响应);如果服务端同意升级,那么需要这样响应:

HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: protocol-name[/protocol-version]

[... data defined by new protocol ...]

HTTP Upgrade 响应的状态码是 101,并且响应正文可以使用新协议定义的数据格式。

升级HTTP 2.0的请求响应也是和上边一样。
HTTP 1.1 升级请求例子:

GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: 

如果服务器不支持HTTP 2.0,直接返回HTTP 1.1响应,例子:

HTTP/1.1 200 OK
Content-Length: 243
Content-Type: text/html
····

如果服务器支持HTTP 2.0,那就可以回应 101 状态码及对应头部,并且在响应正文中可以直接使用 HTTP/2 二进制帧:

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c

[ HTTP/2 connection ... ]

ALPN扩展

HTTP 2.0 协议本身并没有要求它必须基于 HTTPS部署,但是因为主流浏览器都只支持基于HTTPS部署的HTTP 2.0所以变相的强制了必须基于HTTPS才能升级,除非只给自己客户端使用。
HTTPS升级HTTP 2.0使用ALPN扩展字段定义升级,因为HTTPS引入了TLS,所以客户端和服务器端在成功进行TLS连接之后才能发送应用数据。而要建立 TLS 连接,本来就要进行一些参数的协商。引入 HTTP/2 之后,需要做的只是在原本的协商机制中把对 HTTP 协议的协商加进去即可。


可以看到,客户端在建立 TLS 连接的 Client Hello 握手中,通过 ALPN 扩展列出了自己支持的各种应用层协议。其中,HTTP/2 协议名称是 h2

如果服务端支持 HTTP/2,在 Server Hello 中指定 ALPN 的结果为 h2 就可以了;如果服务端不支持 HTTP/2,从客户端的 ALPN 列表中选一个自己支持的即可。

并不是所有 HTTP/2 客户端都支持 ALPN,理论上建立 TLS 连接后,依然可以再通过 HTTP Upgrade 进行协议升级,只是这样会额外引入一次往返。

HTTP 2.0的支持情况

客户端支持情况

可以访问https://caniuse.com/#search=http2查看浏览器支持情况,这个截图是2019年7月24日的情况。
HTTP 2.0_第15张图片

服务器支持情况

  • Nginx 1.9.5+ 支持HTTP 2.0
  • Apache Tomcat 8.5+ 支持HTTP 2.0
  • Apache HTTP Server 2.4.17+ 支持HTTP 2.0
  • 还有的其他可以访问这个页面,记录了很多服务器和客户端对HTTP 2.0的支持情况

查看网站是否启用 HTTP 2.0

首先,要辨别某个网站是否启用了 HTTP 2.0 ,可以通过浏览器开发工具的「网络」面板中的 Protocol 字段查看。也可以通过扩展在浏览器地址栏显示当前的协议类型,HTTP/2 指示器扩展:Chrome 版、Firefox 版。安装后,访问网站是如果出现下图这种,就是HTTP 2.0.
在这里插入图片描述
如果是灰色的,如下图就不是HTTP 2.0
在这里插入图片描述
也可以使用chrome调试板查看,如图:
在这里插入图片描述

你可能感兴趣的:(计算机网络)