http2

HTTP/2(最初名为 HTTP/2.0)是 WWW 使用的 HTTP 网络协议的主要版本。 它来自早先由 Google 开发的实验性 SPDY 协议。 HTTP/2由互联网工程任务组的超文本传输协议工作组 httpbis(其中 bis 表示“第二个”)开发。 HTTP/2是 HTTP 1.1以来的第一个新版本,于1997年在 RFC 2068中进行了标准化。工作组于2014年12月向 IESG 提交了 HTTP/2作为建议标准的审议意见,IESG 批准其发布为提议 标准于2015年2月17日发布。HTTP/2规范于2015年5月发布为 RFC 7540。

Chrome,Opera,Firefox,Internet Explorer 11,Safari,Amazon Silk 和 Edge 浏览器支持标准化工作。 到2015年底,大多数主流浏览器都增加了对 HTTP/2 的支持。

官方协议

RFC 7540 - Hypertext Transfer Protocol Version 2(HTTP/2)
RFC 7541 - HPACK: Header Compression for HTTP/2

演示

HTTP/1.1 vs HTTP/2

HTTP/1缺陷

  • 一个TCP链接上只能有一个请求/响应
  • 头部消耗
    HTTP/1 协议头部使用纯文本格式,没有任何压缩,且包含很多冗余信息(例如 Cookie、UserAgent 每次都会携带),所以一个页面的请求数越多,头部带来的额外开销就越大。

如打开http://www.oschina.net/question/1397765_172789?fromerr=VbVRMMxc
传输头部的开销超过了5%

更有为了获得2字节的数据,在头部传输上花费了几十倍的流量:

Why do we need header compression?

Mozilla的Patrick McManus 通过计算消息头对平均页面负载的影响,给出下面的说明:

假定一个页面有80个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1400字节的消息头(同样这也并不少见,因为Cookie和引用等东西的存在), 至少要7到8个来回去“在线”获得这些消息头。
这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已.
这全都由于TCP的慢启动机制,它会基于对已知有多少个包,来确定还要来回去获取哪些包 – 这很明显的限制了最初的几个来回可以发送的数据包的数量.
相比之下,即使是头部轻微的压缩也可以是让那些请求只需一个来回就能搞定——有时候甚至一个包就可以了。
这种开销是可以被节省下来的,特别是当你考虑移动客户端应用的时候,即使是良好条件下,一般也会看到几百毫秒的来回延迟。

HTTP/2改进

1.多路复用,只需一个连接即可实现并行

HTTP/2引入"流"概念支持多路复用。
流:

是服务器和客户端在HTTP/2连接内用于交换帧数据的独立双向序列,逻辑上可看做一个较为完整的交互处理单元,即表达一次完整的资源请求-响应数据交换流程。
一个业务处理单元,在一个流内进行处理完毕,这个流生命周期完结。

流与HTTP2连接关系

实际传输可能是这样的

流的一些属性:

  • 一个HTTP/2连接可同时保持多个打开的流,任一端点交换帧;
  • 流可被客户端或服务器单独或共享创建和使用;
  • 流可被任一端关闭;
  • 在流内发送和接收数据都要按照顺序;
  • 流的标识符自然数表示,1~2^31-1区间,由创建流的终端分配;
  • 流与流之间逻辑上是并行、独立存在;

2.服务器"推送"

"推送"是HTTP/2添加的一种新的交互模式,允许服务端“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。比如:客户端浏览页面请求page.html,服务器在发回page.html后,认为客户端还需要JavaScript和CSS,则把strict.js和style.css也推送给客户端。当然这个“推送”其实不是你想推就能推的,服务器要遵循请求-响应这个模型,只不过服务器对同一请求可以推送多个响应。客户端在交换 SETTINGS 帧时,设置字段 SETTINGS_ENABLE_PUSH(0x2) 为1显式允许服务器推送。

3.头部压缩

为了减少传输头部在整个HTTP请求中所占的比重,HTTP/1 时代,有很多优化方案可以尝试,例如合并请求、启用 Cookie-Free 域名等等,但是这些方案或多或少会引入一些新的问题。

HTTP/2协议使用了HPACK算法压缩头部,能显著压缩头部大小。

4.优先级

浏览器请求优先级与 HTTP 2.0

  • 浏览器在渲染页面时,并非所有资源都具有相同的优先级:HTML 文档本身对构建 DOM 不可或缺,CSS 对构建 CSSOM 不可或缺,而 DOM 和 CSSOM 的构 建都可能受到 JavaScript 资源的阻塞,其他资源(如图片)的优先级都可以降低。
  • 为加快页面加载速度,所有现代浏览器都会基于资源的类型以及它在页面中的位置排定请求的优先次序,甚至通过之前的访问来学习优先级模式——比如,之前的渲染如果被某些资源阻塞了,那么同样的资源在下一次访问时可能就会被赋予更高的优先级。
  • 在 HTTP 1.x 中,浏览器极少能利用上述优先级信息,因为协议本身并不支持多路复用,也没有办法向服务器通告请求的优先级。此时浏览器只能依赖并行连接,且最多只能同时向一个域名发送 6 个请求。于是,在等连接可用期间,请求只能在客户端排队,从而增加了不必要的网络延迟。理论上,HTTP 管道可以解决这 个问题,只是由于缺乏支持而无法付诸实践。
  • HTTP 2.0 一举解决了所有这些低效的问题:浏览器可以在发现资源时立即分派请求,指定每个流的优先级,让服务器决定最优的响应次序。这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。

HTTP/2常见问题解答FAQ

HTTP/2实现情况

客户端对HTTP/2的支持

iOS

  • NSURLConnection
  • NSURLSession (HTTP/2 is only supported by NSURLSession. NSURLConnection has been deprecated in iOS 9. The HTTP/2 support is part of iOS 9, and thus won't be available on earlier OS releases.)

    • on iOS 9, via HTTP/2
    • on iOS 8, via HTTP/1.1 or SPDY
    • on iOS 7, via HTTP/1.1
  • AFNetworking 3.0版本已经支持NSURLSession(即 支持HTTP/2)
  • SDWebImage 要到4.0.0版本 才会引入 NSURLSession Refernce

Android

  • HttpUrlConnection 不支持
  • OkHttp 完整支持
  • Volley 可配置使用OkHttp传输数据

Windows

HTTP/2 support is present in Windows 10 and the Server 2016 Technical Preview

浏览器


参见:CanIUse

服务端对HTTP/2的支持

Nginx

Nginx 1.9.5+ 完整支持:

版本要求:

openssl 1.0.2+ (OpenSSL 1.0.2 开始支持 ALPN)
Nginx 1.9.5+

Nginx 编译安装:

./configure \
    --prefix=/usr/local/nginx \
    --with-openssl=/usr/local/openssl-1.0.2e \
    --with-pcre \
    --with-stream --with-stream_ssl_module \
    --with-http_ssl_module \
    --with-http_v2_module \
make 
make install

Moving to HTTP/2 with NGINX 1.9.5

Tengine

Tengine 2.1.2+ 支持 http2。

协议协商

在完全过度到 http/2 协议,http1.1 的服务器和客户端依然大量存在,这就注定新老协议的一个共存。这样,浏览器和服务器就需要协商使用何种协议进行通讯。

h2c

即:HTTP/2 ClearText。直接在标准TCP之上建立HTTP2,采用“h2c”作为协议标识符,在未知服务器是否提供HTTP/2支持之前,可以依赖现有HTTP/1.1进行试探。

GET /page HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c 
HTTP2-Settings: (SETTINGS payload) 

HTTP/1.1 200 OK 
Content-length: 243
Content-type: text/html

(... HTTP/1.1 response ...)

          (or)

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

(... HTTP/2 response ...)

h2

HTTP/2安全版本在TLS上构建,使用ALPN扩展协议进行协商,采用“h2”作为协议标识符,在TLS层协商过程中一并完成对是否支持HTTP2的试探。

ALPN 和 NPN 区别:
NPN(Next Protocol Negotiation) 是一个使 SPDY 在 TLS 服务器上对使用何种应用层协议进行协商的协议。ALPN(Application Layer Protocol Negotiation) 则是 IETF(h2的标准化组织)修改 NPN 而成的一个标准。两者区别在于谁持有会话协议的决定权,ALPN 是由客户端给服务器发送一个协议清单,由服务器来最终选择一个,而 NPN 则正好相反。

h2 和 h2c 的区别

理论上来说,http2 可以架构在 tls 上,也可以架构在 tcp 上(运行于非加密通道之上的 http2)。协议文本也确实没有限定或者强制使用 tls 信道。但是 http2 的前身是 spdy,而 spdy 是在 tls 之上的。目前 chrome/firefox 也只支持 tls 。这就让架构于 tls 之上就成为 http2 的事实上的标准。

调试工具

Charles

Charles4.0+开始支持http2,如使用Charles4.2抓街电的包:

Wireshark

Wireshark 是一个无比强大的网络封包分析工具。新版 Wireshark 才增加了对 HTTP/2 的支持,下载地址:

https://www.wireshark.org/#download

安装后,输入过滤条件进行抓包,如:

由于使用TLS,数据是加过密了的,看不到协议细节。为了解密,首先需要保存ssl key,然后 wireshark 通过 key 来解密数据包。具体操作请参考>>

解密后如图所示:

CURL

执行命令 curl --version 查看curl是否支持 http2。如返回:

curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets

可以看到当前 curl 的版本及支持的协议以及功能特性没有支持 HTTP2。为了让 curl 支持 HTTP2 需要安装 nghttp2(http2 的 C 语言库)。\
编译安装nghttp2(MAC 可以直接通过 brew install nghttp2 安装):

git clone https://github.com/tatsuhiro-t/nghttp2.git
cd nghttp2
autoreconf -i
automake
autoconf
./configure
make
sudo make install

升级curl版本:

wget https://curl.haxx.se/download/curl-7.56.0.tar.bz2
tar -xvjf curl-7.56.0.tar.bz2
cd curl-7.56.0
./configure --with-nghttp2 --with-ssl
sudo make && make install

检查是否安装成功:

curl --version

测试curl with http2:

$ curl --http2 -I https://www.jd.com
HTTP/2.0 200
server:JDWS/2.0
date:Tue, 17 Oct 2017 07:53:14 GMT
content-type:text/html; charset=utf-8
content-length:122981
vary:Accept-Encoding
vary:Accept-Encoding
expires:Tue, 17 Oct 2017 07:53:00 GMT
cache-control:max-age=30
ser:130.26
via:BJ-M-YZ-NX-76(HIT), http/1.1 CD-CT-2-JCS-25 ( [cRs f ])
age:30
strict-transport-security:max-age=360

nghttp

nghttp 做为一个功能完整的 HTTP/2 客户端,非常适合用来查看和调试 HTTP/2 流量。它支持的参数很多,通过官方文档或者 nghttp -h 都能查看。最常用几个参数如下:

  • -v, --verbose,输出完整的 debug 信息;
  • -n, --null-out,丢弃下载的数据;
  • -a, --get-assets,下载 html 中的 css、js、image 等外链资源;
  • -H, --header=<HEADER>,添加请求头部字段,如 -H':method: PUT';
  • -u, --upgrade,使用 HTTP 的 Upgrade 机制来协商 HTTP/2 协议,用于 h2c,详见下面的例子;
    以下是使用 nghttp 访问 nghttp2 官网的结果。从调试信息中可以清晰看到 h2c 协商以及 Server Push 的整个过程:
$ nghttp -nvu http://nghttp2.org
[  0.244] Connected
[  0.244] HTTP Upgrade request
GET / HTTP/1.1
host: nghttp2.org
connection: Upgrade, HTTP2-Settings
upgrade: h2c
http2-settings: AAMAAABkAAQAAP__
accept: */*
user-agent: nghttp2/1.24.0


[  1.833] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c


[  1.833] HTTP Upgrade success
[  1.835] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):1048576]
          [SETTINGS_HEADER_TABLE_SIZE(0x01):8192]
[  1.835] recv (stream_id=1) :status: 200
[  1.835] recv (stream_id=1) date: Tue, 17 Oct 2017 08:07:13 GMT
[  1.835] recv (stream_id=1) content-type: text/html
[  1.835] recv (stream_id=1) last-modified: Wed, 20 Sep 2017 14:04:09 GMT
[  1.835] recv (stream_id=1) etag: "59c27559-19e1"
[  1.835] recv (stream_id=1) accept-ranges: bytes
[  1.835] recv (stream_id=1) content-length: 6625
[  1.835] recv (stream_id=1) x-backend-header-rtt: 0.001153
[  1.835] recv (stream_id=1) server: nghttpx
[  1.835] recv (stream_id=1) via: 2 nghttpx
[  1.835] recv (stream_id=1) x-frame-options: SAMEORIGIN
[  1.835] recv (stream_id=1) x-xss-protection: 1; mode=block
[  1.835] recv (stream_id=1) x-content-type-options: nosniff
[  1.835] recv HEADERS frame <length=198, flags=0x04, stream_id=1>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  1.835] recv DATA frame <length=6625, flags=0x01, stream_id=1>
          ; END_STREAM
[  1.835] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  1.835] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

h2load

nghttp2 带有 h2load,它是一个支持 HTTP/2 的压测工具,可以用来测试 HTTP/2 服务的稳定性、QPS 等信息。它支持的参数也可以通过官网文档或者 h2load -h 来查看。下面是一个简单例子:

$ h2load https://example.com -n 100 -c 10
starting benchmark...
spawning thread #0: 10 total client(s). 100 total requests
TLS Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES128-GCM-SHA256
Server Temp Key: ECDH P-256 256 bits
Application protocol: h2
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done

finished in 2.67s, 37.52 req/s, 49.48KB/s
requests: 100 total, 100 started, 100 done, 100 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 100 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 131.87KB (135038) total, 5.50KB (5628) headers (space savings 79.16%), 124.02KB (127000) data
                     min         max         mean         sd        +/- sd
time for request:   174.06ms    219.98ms    188.81ms      8.90ms    61.00%
time for connect:   558.91ms    606.67ms    572.44ms     14.39ms    80.00%
time to 1st byte:   737.95ms    813.75ms    762.35ms     22.60ms    60.00%
req/s           :       3.75        4.30        4.07        0.16    60.00%

Chrome开发者工具

Chrome 40版本就开始支持http2了,如图所示:

Firefox开发者工具

Firefox 开发者工具也支持http2,如图所示:

浏览器插件

HTTP/2 and SPDY indicator 是一款可以为每个网站提供HTTP/2,SPDY 和 QUIC 支持的指示按钮的插件,也有管理页面便于查看更详细的信息。

Chrome浏览器安装好插件后,在地址栏中输入 chrome://net-internals/#http2 会列出浏览器当前所有活跃的 HTTP/2 Session,点击具体的 Session ID,可以看到全部帧信息,如图所示:

参考