鉴于HTTP/2的性能改进及主流浏览器都以支持,新浪云计划在近期对企业用户的部分上传了SSL的证书的域名开放使用HTTP/2功能,此功能默认关闭,需要用户自行开启。
HTTP/2诞生的背景
当前几乎所有互联网上的网页内容传输都采用了HTTP/1.1协议,随着网页内容和样式的发展,HTTP/1.1协议的劣势逐渐明显。
于是在2014年,HTTPbis小组决定开始定制HTTP/2协议。而早在2012年Google就设计了SPDY协议用来解决HTTP/1.1的缺陷,并被用于Google Chrome浏览器中来访问Google的SSL加密服务,据官方说明使用SPDY协议页面加载时间相比于HTTP/1.x减少了64%。因此,HTTPbis便基于SPDY/3草案进行一些修改之后发布了HTTP/2的draft-00。
HTTP/2简介
- 继续维持HTTP/1.1的模型。客户端基于TCP协议发送请求到服务器。
- 不改变
http://
和https://
协议的URL。 - 新的二进制格式。
HTTP/1.1是明文协议,其格式由三部分组成:start line(request line或者status line),header,body。要识别这3部分就要做协议解析,而基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多。二进制则不同,只认0和1的组合。基于这种考虑HTTP/2的协议解析决定采用二进制格式,实现方便且健壮。
HTTP/2会发送有着不同类型的二进制帧,但他们都有这5个公共字段:Type, Length, Flags, Stream Identifier和frame payload。
规范中一共定义了10种不同的帧,其中最基础的两种分别对应于HTTP/1.1的DATA和HEADERS。虽然协议的格式和HTTP/1.1完全不同,实际上HTTP/2并没有改变HTTP/1.1的语义,只是把原来HTTP/1.1的header和body部分用frame重新封装了一层而已。
- 连接的多路复用。
二进制协议中的Stream Identifier字段就是用作连接共享机制的。一个request对应一个stream并分配一个Identifier,这样一个连接上可以有多个stream,每个stream的frame可以随机的混杂在一起,接收方可以根据Stream Identifier将frame再归属到各自不同的request里面。
- 支持优先级和依赖。
HTTP/2里的每个stream都可以设置优先级(Priority)和依赖(Dependency)。优先级高的stream会被server优先处理和返回给客户端,stream还可以依赖其它的sub streams。优先级和依赖都是可以动态调整的。动态调整在有些场景下很有用,假想用户在用你的app浏览商品的时候,快速的滑动到了商品列表的底部,但前面的请求先发出,如果不把后面的请求优先级设高,用户当前浏览的图片要到最后才能下载完成,显然体验没有设置优先级好。同理依赖在有些场景下也有妙用。
- 头压缩。 HTTP/1.1的header带有大量信息,而且每次都要重复发送,HTTP/2使用HPACK压缩算法来减少需要传输的header大小。通讯双方各自缓存一份header字段列表,既避免了重复header的传输,又减小了需要传输的大小。
- 重置连接。
很多使用场景有取消图片下载的功能,对于HTTP/1.1来说最终解决方案都会导致断开连接,下次再发请求就必须重新建立连接。而HTTP/2引入RST_STREAM类型的frame,可以在不断开连接的前提下取消某个request的stream。从而避免浪费带宽和中断已有的连接。
- 流量控制。
- 服务器推送。
现状
到目前为止几乎所有的主流浏览器和web服务器都支持HTTP/2,iOS9+也已支持HTTP/2。
可以看出来,连接的多路复用和重置连接使页面加载时长缩短,支持优先级和依赖使用户体验更好,头压缩和重置连接使降低了传输流量。
开启前的注意事项
HTTP/2协议实现中,所有的客户端会把header头中的key转换为小写,如果您的程序中使用了类似以下的代码(以PHP为例)则需要修改后再开启。
PHP中如果直接使用$_SERVER
获取请求中的header信息,以HTTP_
开头的部分都会被转换成大写。如果程序中所有的header头都是从这里获取的,则不需要修改代码。
如果使用了apache_request_headers
、apache_getenv
函数从Apache层获取原始的header信息,且之前的程序中区分了大小写,需要修改成为把所有的key值都改成小写后才能获取。
测试的程序如下,从服务端写一段脚本:
<?php
var_dump(apache_request_headers());//输出从Apache获取的header信息
var_dump("------PHP HEADER-----\n");
var_dump($_SERVER);//输出从$_SERVER中获取的header信息
用curl测试下自定义header试试,如何让curl支持http2请参考下一小节,有兴趣的可以从新浪云测试。 运行命令:
curl --http2 'https://www.aikaiyuan.com/test.php' -H"A:bBb"
看到执行的结果如下:
array(18) {
["X-Forwarded-For"]=>
string(30) "220.181.136.57, 220.181.136.57"
.....
["a"]=>
string(3) "bBb"
}
string(22) "------PHP HEADER-----
"
array(46) {
["MEF_PROXY_ADDR"]=>
string(11) "10.67.15.15"
.....
["HTTP_A"]=>
string(3) "bBb"
.....
}
可以清楚的看到实验结果,从apache_request_headers
函数中取到的原始值中header值A
已经被转换成a
了,但是在$_SERVER
中之所以还是大写,是因为PHP自身的处理。
用curl测试HTTP/2
如果你也希望可以从curl中测试HTTP/2,可以参考以下的步骤编译一个支持HTTP/2的curl客户端。
从新浪云创建一个Ubuntu应用
访问 http://sae.sina.com.cn/?m=apps&a=create ,语言选择“自定义”,部署方式选择“手工部署”,操作系统选择“Ubuntu”即可,如图所示:
进入终端
进入应用管理面板,选择左侧“容器管理”,进入应用的容器管理,看到操作中有“终端”,进入终端即可。如图:
安装依赖
依次执行以下的命令安装:
安装相关工具:
apt-get install git g++ make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev libjemalloc-dev cython python3-dev python-setuptools
安装nghttp2
git clone https://github.com/tatsuhiro-t/nghttp2.git
cd nghttp2
autoreconf -i
automake
autoconf
./configure
make
sudo make install
编译安装curl
cd ~
apt-get install wget
wget http://curl.haxx.se/download/curl-7.46.0.tar.bz2
tar -xvjf curl-7.46.0.tar.bz2
cd curl-7.46.0
./configure --with-nghttp2=/usr/local --with-ssl
make && make install
echo '/usr/local/lib' > /etc/ld.so.conf.d/local.conf
ldconfig
验证
验证下curl客户端:
root@67994995f68d:/curl-7.46.0# curl --version
curl 7.46.0 (x86_64-pc-linux-gnu) libcurl/7.46.0 OpenSSL/1.0.2g zlib/1.2.8 nghttp2/1.20.0-DEV
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
找个支持HTTP/2的站点(请添加host 61.172.201.142 www.aikaiyuan.com,因为当前新浪云正在灰度)测试一下:
curl --http2 'https://www.aikaiyuan.com/test.php' -H"a:b" >/dev/null -vvv
---中间部分输出省略---
> GET /test.php HTTP/1.1
> Host: www.aikaiyuan.com
> User-Agent: curl/7.46.0
> Accept: */*
> a:b
>
{ [5 bytes data]
< HTTP/2.0 200
< server:nginx
< date:Wed, 15 Feb 2017 10:14:26 GMT
< content-type:text/html; charset=UTF-8
< content-length:3038
< via:144157
<
{ [1262 bytes data]
100 3038 100 3038 0 0 9655 0 --:--:-- --:--:-- --:--:-- 9706
* Connection #0 to host www.aikaiyuan.com left intac
可以看到已经可以使用HTTP/2.0协议发送请求了。