上篇文章深入浅出:5G和HTTP里给本身挖了一根深坑,说是要写一篇关于HTTP/2的文章,今天来还帐了。css
本文分为如下几个部分:html
HTTP/2主要是为了解决现HTTP 1.1性能很差的问题才出现的。当初Google为了提升HTTP性能,作出了SPDY,它就是HTTP/2的前身,后来也发展成为HTTP/2的标准。nginx
HTTP/2兼容HTTP 1.1,例如HTTP Method,Status code,URI以及大部分Header Fields。git
HTTP/2经过如下方法减小latency,用来改进页面加载的速度,github
HTTP/2支持HTTP 1.1里的大部分use case,例如桌面浏览器、移动浏览器、Web API、Web Server、代理服务器、反向代理服务器、防火墙和CDN等。web
HPack是HTTP/2 里HTTP头压缩的算法,具体能够参看https://tools.ietf.org/html/rfc7541。下面简单介绍一下HPack是如何工做的。算法
见下图,该图来自Google 的性能专家 Ilya Grigorik 的文章HTTP/2 is here, let's optimize!,它很是直观地描述了 HTTP/2 中头部压缩的原理:chrome
简单说,HTTP头压缩须要在HTTP/2 Client和服务端之间:浏览器
在HTTP头里,有些key:value是固定,例如:缓存
:method: GET
:scheme: http
在编码时,它们直接用一个index编号代替,例如:method:GET是2,这些在一个静态表定义。静态表的定义以下,总共61个Header Name,点击URL https://tools.ietf.org/html/rfc7541#appendix-A查看全部静态表的定义。
Index | Header Name | Header Value |
---|---|---|
1 | :authority | |
2 | :method | GET |
3 | :method | POST |
4 | :path | / |
5 | :path | /index.html |
6 | :scheme | http |
7 | :scheme | https |
8 | :status | 200 |
... | ... | ... |
32 | cookie | |
... | ... | ... |
60 | via | |
61 | www-authenticate | |
使用静态表、动态表、以及Huffman编码能够极大地提高压缩效果。对于静态表里的字段,原来须要N个字符表示的,如今只须要一个索引便可,对于静态、动态表中不存在的内容,还可使用哈夫曼编码来减少体积。HTTP/2 标准里也给出了一份详细的静态哈夫曼码表(https://tools.ietf.org/html/rfc7541#appendix-B),它们须要内置在客户端和服务端之中。
关于HPack的算法和实现,后面专门抽一篇文章来写。
HTTP/2协议里有个negotiation的机制,让客户端和服务器选择使用HTTP 1.1仍是2.0,这个是由ALPN来实现,关于ALPN,能够参看
ALPN(Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension,https://tools.ietf.org/html/rfc7301。
下面是抓包截图,在TLS里的Client Hello的包里,咱们能够看到ALPN里由H2和HTTP/1.1,这就是说客户端支持HTTP2以及HTTP 1.1.
当Server收到后,会识别Client发过来的协议列表,若是不认识就忽略掉。若是认识多个,则选择一个最合适的协议发布给Client。也是在Server Hello里的ALPN返回,见下图。
Server Push是HTTP 2最重要的一个特性。
在HTTP 1.1里,在同一个 TCP 链接里面,上一个回应(response)发送完了,服务器才能发送下一个,但在HTTP/2里,能够将多个回应一块儿发送。
下图是PUSH模式,当请求一个HTML时,若是HTML里有CSS文件,server会一并推给client,而不像在HTTP 1.1下,还须要再发一个CSS的请求。
根据上图,从理论上PUSH模式下性能会好不少。
举个例子解释一下。下面是一个简单的HTML页面,假说是index.html 。
<html> <head> <link rel="stylesheet" href="style.css"> </head> <body> <p>This is a sample to illustrate how HTTP/2 works</p> <img src="example.png"> </body> </html>
这里有三个文件须要处理:该HTML页面、CSS文件style.css以及图片example.png。在HTTP 1.1里为了处理这三个文件,Client须要发三个请求给Server。
首先,发送一个请求index.html,
GET /index.html HTTP/1.1
Client解析该HTML文件,继而知道有2个style.css和example.png资源文件下载。
Client继续发送2个请求下载他们。
GET /style.css HTTP/1.1
以及
GET /example.png HTTP/1.1
通常为了解决这两个问题,像CSS文件,能够把CSS code直接放在HTML里,也能够把example.png转化为base64 code嵌入在HTML里,以上只是把外部资源文件合并到HTML里。
除了上述方法,还有一个优化的方法,就是Preload(预加载),能够参看这里,https://w3c.github.io/preload/。
因此咱们能够把HTML代码改为以下:
<link rel="preload" href="/styles.css" as="style"> <link rel="preload" href="/example.png" as="image">
那Preload是什么意思呢?就是说下载前一个页面时,能够把相关的资源文件预先加载好,这样感受起来会快一些。可是有一个关键问题须要注意,即使是预加载的状况下,也不能减小HTTP请求次数。
针对上面的问题,咱们引出服务器推送(server push)。根据上面的图,咱们能够看出,Server尚未收到Client的请求,就把各类资源推送给Client。
拿上面例子继续举例,当Client只请求index.html
,可是Server把index.html
、style.css
、example.png
所有发送给浏览器。这样只须要一轮 HTTP 通讯,Client就获得了所有资源。
如今主流的软件都支持HTTP/2.
浏览器
基本上大部分浏览器在2015年末都支持HTTP/2了,包括Chrome、Opera、Firefox、IE 十一、Safari,Edge。
在Chrome上,能够下载插件HTTP Indicator,判断访问的网站是否支持HTTP/2.
也能够打开Chrome的开发者工具,打开Network tab,能够看到Protocol为h2的就是HTTP/2请求。若是Initiator为push的,说明开启了Server Push模式。
经常使用Server软件:
硬件:
CDN/Cloud:
若是开启了Server Push模式,咱们很容易意识到一个问题,那就是缓存问题。Server见到HTML页面就把外部资源push给Client,若是没有缓存,其实很浪费。为了解决这个问题,能够在第一次请求时push,后面的请求都不push了。
服务器推送有一个很麻烦的问题。所要推送的资源文件,若是浏览器已经有缓存,推送就是浪费带宽。即便推送的文件版本更新,浏览器也会优先使用本地缓存。下面是 Nginx 官方给出的示例,根据 Cookie 判断是否为第一次访问(https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/)。
server { listen 443 ssl http2 default_server; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; http2_push_preload on; location = /demo.html { add_header Set-Cookie "session=1"; add_header Link $resources; } } map $http_cookie $resources { "~*session=1" ""; default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
有人专门作过测试,https://www.smashingmagazine.com/2017/04/guide-http2-server-push/#measuring-server-push-performance,借用该文的一张图片,
能够看出,启用HTTP/2后性能并未大幅度提高,因此在使用HTTP/2仍是谨慎一些,若是使用不当,反而会使性能降低。
另外,Ngnix专门撰文描述7个提升HTTP/2的技巧https://www.nginx.com/blog/7-tips-for-faster-http2-performance/ 。
参考文章: