标签 spdy 下的文章

SPDY真的快吗?

SPDY真的快吗?

Google SPDY协议(现已加入HTTP2.0草案)宣称能够提升浏览器速度一倍,HTTPS同SPDY协议在前端加载速度上的对比如下(参考极客公园):

8bb3b4b0e21d86701f1ebd880768ec84.gif

针对该优点,小伙伴进行了对比测试,发现http协议SPDY协议在前端响应速度上没太大出入。

Protocol 1 2 3 4 5 Avg
http 35.91 35.80 35.86 36.95 37.80 36.46
spdy 36.26 36.21 36.24 36.27 36.30 36.26

注:所有统计时间均在chrome下禁用缓存并开启调试模式进行,时间单位为秒。

看到这个测试,心都凉了半截,为什么spdy的加载时间和http的加载时间相差无几,完全没有Google宣称的那般美好,甚至大部分的耗时比http还长,感觉Google忽悠了整个世界,再也无法相信美帝国主义的公司。

面对他的这个测试报告,我只能说一定是开门的方式不对。

先来说结论:SPDY无法改善用户的上网带宽,用这么大的图片来测试,时间基本都花在传输上了,自然难以看出区别。

传统浏览器慢在哪里

要了解SPDY到底快在哪里,先得看HTTP协议性能都消耗在了哪里,干脆我们看看传统的浏览器都需要把时间花到哪里:

  1. DNS查询时间(把域名转成IP地址,一般从几毫秒到几秒不等,移动网络中此过程有时需要十几秒)
  2. TCP握手时间(取决于你和服务器之间的Ping有多高,TCP的三次握手决定了每建立一个TCP连接都需要三倍单程时间)
  3. TCP慢启动窗口时间(刚建立的TCP连接不会以最大速度发送数据,数据传输速度会有一个慢慢增长的过程)
  4. HTTP请求发送时间(一般的GET请求头有几百字节到几千字节)
  5. 等待服务器处理时间(读取磁盘,查询数据库,运行服务端程序逻辑等等,一般几毫秒到几秒不等)
  6. HTTP回应接收时间(下载服务器返回的数据,一般文本内容只有几KB到几十KB,图片等内容可能有几MB)
  7. 浏览器解析处理时间(处理DOM,排版,屏幕绘图等等)

那么Google又针对以上几种时间消耗又分别做了什么优化呢?

  1. Chrome浏览器会Cache下来DNS查询结果,Google甚至鼓励你用Google的DNS服务器。
  2. 网页总有许多资源需要加载,包括CSS、JS、图片等,为了并行加载提高效率,现代浏览器会为每个域名建立6个TCP连接,每个TCP连接都需要经历握手过程。
  3. 每个TCP连接都会经历慢启动的过程,Google也没办法,因为TCP的慢启动策略是操作系统管理的。不过现代浏览器都实现了HTTP/1.1的Keep-Alive支持,能用一个连接发送多个请求,虽然由于复杂度的原因Pipeline基本都没有实现,但也能有效避免像HTTP/1.0那样每传输一项资源都重新建立一次连接。
  4. 第4、第6点Chrome是无法优化的,因为HTTP协议就那么定的。
  5. 第5点掌握在网站开发者手中,Google虽然无力改变别的网站,但他可以把自家的网站优化好,比如YouTube、Picasa、Gmail等等Google应用都有做诸多优化,响应速度也是业内领先的。
  6. 浏览器解析处理方案,Chrome下了不少工夫,先是借力Webkit的轻巧高效,又有引入强大的带JIT的V8 JavaScript解释器,后来更是搞出了Blink渲染引擎,可谓Webkit的再优化版。

SPDY到底快在哪里

当Google发现自己把能做的都做了之后,只能向他们掌控不了的领域动刀,前面时间消耗的第2、3、4、6点都值得从协议上优化,于是Google抛出了SPDY协议。

  1. SPDY支持多路复用。并行化的好处显而易见,Google Chrome为每个域名开6个TCP就会让服务器需要维护的TCP连接数量上涨到三倍,而且每个连接都要经历三次握手和慢启动过程。SPDY干脆就只用一个TCP连接,这样三次握手和慢启动过程只走一遍,因为支持多路复用,所以SPDY仍可以享受并行化加载的好处,Pipeline与否也显得无关紧要。
  2. SPDY支持优先级设定,对于渲染页面而言,关键的CSS和图标资源可以优先加载,非关键的CSS和JS以及图片可以低优先级加载,这样可以帮助浏览器提高渲染效率。
  3. SPDY支持HTTP头压缩,并强制启用正文压缩,能有效减少请求和回应的数据量,优化HTTP的数据传输量。
  4. 此外,SPDY还有Server Push这种需要服务器开发人员支持的大杀器帮助提升速度。一般浏览器都是先下载HTML,然后看到里面有引用某CSS再去请求这个CSS,解析CSS看到里面需要一张图片再去请求这张图片。SPDY协议允许网页开发者直接把这些次生资源主动Push到浏览器中,这样当浏览器解析HTML发现需要一个CSS的时候,会发现这个CSS已经下载好了,不用再发请求下载了,解析CSS发现需要某张图片的时候,发现它也已经下载好了。
  5. 对于第二次来访者,考虑到次生资源可能已经有浏览器缓存,SPDY允许服务器发送Server Hint,告诉浏览器本网页需要哪些资源,没Cache的直接下载,不用等解析到才来请求。这样也能有效提升速度。

SPDY慢在哪里

俗话说有得必有失,SPDY也是有额外开销的,这些开销会减慢它的速度。那么SPDY的开销在哪里呢?和HTTP协议相比,SPDY的额外开销只有一个,那就是它全程使用SSL/TLS,存在证书交换时间。和HTTPS差不多,每次浏览器连上SPDY服务器之后,先会收到一个长达几KB的SSL安全证书,然后浏览器还要计算证书签名是否合法,并搜索一遍证书吊销记录中有没有这个证书的指纹信息,然后才开始生成密钥传输数据。即便是现代计算机,这个过程也会花掉几十到几百毫秒的时间。

正确的测试方法

由于Server Push和Server Hint这种大杀器都需要服务端跟踪记录每个网页会引用到的资源,并确定用户浏览器是否首次来访,不容易准备测试用例也不容易在自己的服务中实施,暂时先不考虑。要对照测试SPDY和HTTP的性能差异,至少应该用接近真实网页的例子,或者用比较能突出并行化优势的例子。像前面小伙伴测试的例子,主要性能开销都在网络传输二进制图片文件之上,SPDY的性能优势被全部淹没在了统计误差之中。

极客公园文章中用GIF演示的例子是一个偏向SPDY优势的例子,大量的小图标文件非常适合高度并行化的传输,而且几百字节的HTTP Header和大量不到1KB的图片文件相比,额外的负载显得如此严重,SPDY的头压缩功能可以很有效地降低传输数据量,HTTP的并行度也远无法和SPDY相提并论,非常容易看到SPDY在性能提升上的效果。考虑到实际的确会有一些网页会出现这样的情况(比如国家/地区选择页),这个例子也不算太背离实用场景。当然,真正的前端高手遇到这种情况会选择使用CSS Image Sprite技术来减少HTTP请求量,提升加载速度。不过如果HTTP本身能支持高度并行化地加载,CSS Image Sprite技术也不过是奇技淫巧罢了。

我制作了类似极客公园的例子,大家可以自己测试比较HTTP版本SPDY/HTTP2版本的加载速度差异。(我的测试服务器同时支持SPDY和HTTP2,具体选用哪个协议取决于浏览器)

下面是我测试出来的数据

Protocol 1 2 3 4 5 Avg
http 8.52 7.65 8.25 7.48 8.00 7.98
spdy 3.06 2.73 2.69 2.87 2.74 2.82

我取用南方周末的首页制作了另一个测试用例,这是典型的新闻类网站首页的情况,大家可以自行比较HTTP版本SPDY/HTTP2版本的性能。下面是我的数据:

Protocol 1 2 3 4 5 Avg
http 2.94 2.09 2.04 3.21 2.71 2.60
spdy 3.15 2.79 2.66 1.99 2.35 2.59

这份数据告诉我们,像南方周末这样新闻类网站,大图片一堆一堆的,SPDY并不能提升多少访问速度,至少速度的提升是无法感知的。用Page Speed Insight测试之后可以发现,南方周末网站首页的新闻图片大都可以在不损失画质的前提下压缩超过80%的文件大小。

总结

SPDY协议无法改善用户的上网带宽,以数据传输为时间开销的网站测试无法突显出SPDY协议的优势,不过即便是这种情况,SPDY的性能表现也不会比传统的HTTP慢。SPDY协议是Google在自家网站优化到极致之后,把手伸到HTTP协议上而祭出的神器。借它优化自家网站的访问速度之前,还是先把其它该优化的点都充分做好吧。

配合nghttpx修改nginx配置

上次介绍了用nghttp2软件包中的nghttpx反向代理让普通的网站支持HTTP2协议。在安装配置完成后实际使用的过程中遇到了一些问题,本文总结了一下解决方案。

开始遇到的两个问题是:经过nghttpx转发之后,request header中的host字段没有了;还有PHP脚本无法得到远端的IP地址。解决第一个问题的方法是在nghttpx的启动参数中加入--no-host-rewrite来通知nghttpx不要修改host标头;第二个问题可以加入--add-x-forwarded-for参数来通知nghttpx加入代理转发IP记录。

修改后的启动命令如下:

# nghttpx -D -f *,443 -b 127.0.0.1,80 --add-x-forwarded-for \
 --pid-file=/path/to/pidfile.pid --no-host-rewrite --errorlog-syslog \
 /path/to/private.key /path/to/certification.crt

加入-D参数后nghttpx会以守护进程方式运行,加入--errorlog-syslog可以把错误信息输出到系统的syslog中,而不再使用STDOUT作为错误日志输出;加入--pid-file之后可以输出一个nghttpx的进程号文件,方便制作成initrc脚本用于管理。

后来又遇到一个问题是,nginx向PHP的fastcgi服务器发送参数时,不知道前面使用了https协议,导致许多PHP项目自动侦测https的代码无法正常工作。这个问题可以修改nginx的配置来解决:

因为nghttpx会在转发的请求头中加入一个X-Forwarded-Proto标头,所以我们可以在nginx.conf中加入一个if语句来确认是否使用了https:

# under server or location scope
if ( $http_x_forwarded_proto ~* https ) {
    set $var_https on;
}

然后在fastcgi.conf中修改传给PHP解释器的参数:

#fastcgi_param  HTTPS    $https if_not_empty;
fastcgi_param  HTTPS    $var_https if_not_empty;

把原来的https判断方法注释掉,用新的$var_https参数。如果使用的不是PHP,而是Python,也可以用同样的方式修改uwsgi_params配置文件。

PS:另外还有一种方法是判断请求标头via中是否包含nghttpx字样,如果包含就认为是https访问的,方法和前面的相似就不再赘述。

让你的网站支持HTTP2

HTTP2和SPDY

SPDY是Google的研究人员为了加速Web浏览体验搞出来的一个试验性协议,旨在降低网络延迟。据这帮研究人员报告1,使用SPDY协议的网站平均等待时间只有用HTTP/1.1协议网站的几分之一。SPDY协议发展到2.0版的时候突然声名雀起,几乎每个浏览器都开始支持它——没错,连最顽固的微软IE浏览器也支持了2

然而就在这个SPDY一统天下的大好时代,Google宣布放弃SPDY协议3。当然Google只是放弃SPDY这个名字而已,SPDY协议中绝大部分的优化手段都被Google写进了HTTP2标准草案里。如今HTTP2协议定稿在即,Google才大方出来宣言放弃SPDY,力争推动各厂尽快切换到HTTP2上。

虽然HTTP2在浏览器上的普及势如破竹比当年SPDY不惶多让,可惜主流的开源Web服务器跟进并不迅速,Apache的nginx目前都只有SPDY模块的支持。还好spdylay4的开发团队动作迅速,马上提供了HTTP2的实现,让我们可以不必多加改动地把服务器部署上HTTP2。

下面介绍一下怎么让自己的网站同时支持SPDY和HTTP2协议。

准备工作

因为SPDY只能支持SSL/TLS加密的连接,我假设网站已经有证书和密钥。如果网站还没有这两样东西,请按这里

我在使用的是Ubuntu 14.04 LTS 64bit 系统,因为系统没有自带spdylay的软件包,所以需要从源码编译。如果服务器只想支持HTTP2而不想支持SPDY协议,也可以不安装spdylay。直接开始nghttp2的安装。

首先要安装编译环境和spdylay的依赖库:

# apt-get install build-essential pkg-config zlib1g-dev libssl-dev libxml2-dev libevent-dev libevent-openssl-2.0-5

再去spdylay的网站下载spdylay的源代码包:

# wget 'https://github.com/tatsuhiro-t/spdylay/releases/download/v1.3.2/spdylay-1.3.2.tar.xz'

安装spdylay

解包和配置:

# tar xpf spdylay-1.3.2.tar.xz
# cd spdylay-1.3.2
# ./configure

配置完成之后会显示一个配置表:

Version:        1.3.2 shared 9:0:2
Host type:      x86_64-unknown-linux-gnu
Install prefix: /usr/local
C compiler:     gcc
CFLAGS:         -g -O2
LDFLAGS:
LIBS:           -lz
CPPFLAGS:
C preprocessor: gcc -E
C++ compiler:   g++
CXXFLAGS:       -g -O2
CXXCPP:         g++ -E
Library types:  Shared=yes, Static=yes
CUnit:          no
OpenSSL:        yes
Libxml2:        yes
Libevent(SSL):  yes
Src:            yes
Examples:       yes

请注意Libevent(SSL)务必要显示为yes,不然编译出来的软件包里不会包含spdy的反向代理。检查好了就可以编译和安装:

# make && make install
# ldconfig

因为新安装的lib不会马上被系统读到,导致运行spdylay程序出现.so文件找不到的情况,所以需要运行ldconfig让系统重新载入动态链接库。

此时可以运行一下SPDY的反向代理程序,看看是否工作良好:

# shrpx
Usage: shrpx [-Dh] [-s|--client|-p] [-b <HOST,PORT>]
             [-f <HOST,PORT>] [-n <CORES>] [-c <NUM>] [-L <LEVEL>]
             [OPTIONS...] [<PRIVATE_KEY> <CERT>]

A reverse proxy for SPDY/HTTPS.

[FATAL] Too few arguments
       (shrpx.cc:1167)

出现这个提示说明spdylay已经安装正常。

安装nghttp2

先要安装nghttp2依赖的软件包:

# apt-get install libev-dev libjansson-dev libjemalloc-dev python-dev cython

下载nghttp2的源代码包:

# wget 'https://github.com/tatsuhiro-t/nghttp2/releases/download/v0.7.11/nghttp2-0.7.11.tar.xz'

解压和配置:

# tar xpf nghttp2-0.7.11.tar.xz
# cd nghttp2-0.7.11
# ./configure

配置输出一个更长的信息表:

Version:        0.7.11 shared 13:0:8
Host type:      x86_64-unknown-linux-gnu
Install prefix: /usr/local
C compiler:     gcc
CFLAGS:         -g -O2
WARNCFLAGS:
LDFLAGS:
LIBS:
CPPFLAGS:
C preprocessor: gcc -E
C++ compiler:   g++
CXXFLAGS:       -g -O2 -std=c++11
CXXCPP:         g++ -E
Library types:  Shared=yes, Static=yes
Python:
  Python:         /usr/bin/python
  PYTHON_VERSION: 2.7
  pyexecdir:      ${exec_prefix}/lib/python2.7/dist-packages
  Python-dev:     yes
  PYTHON_CPPFLAGS:-I/usr/include/python2.7
  PYTHON_LDFLAGS: -L/usr/lib -lpython2.7
  Cython:         cython
Test:
  CUnit:          no
  Failmalloc:     yes
Libs:
  OpenSSL:        yes
  Libxml2:        yes
  Libev:          yes
  Libevent(SSL):  yes
  Spdylay:        yes
  Jansson:        yes
  Jemalloc:       yes
  Boost CPPFLAGS:
  Boost LDFLAGS:
  Boost::ASIO:
  Boost::System:
  Boost::Thread:
Features:
  Applications:   yes
  HPACK tools:    yes
  Libnghttp2_asio:no
  Examples:       yes
  Python bindings:yes
  Threading:      yes

需要确认Features中的Application是yes。可以编译和安装:

# make && make install
# ldconfig

启动nghttpx反向代理

终于到最后一步,用nghttpx为网站提供HTTP2支持。因为nghttpx是反向代理服务器,如果现在的Web服务器已经启用了https,需要先把https支持关掉,不然会发生端口冲突哦。我们使用nghttpx的Base模式就可以了,在443端口上接收https请求,转发到本机的80端口,在先安装了spdylay的情况下,nghttpx是可以同时支持HTTP/1.1、SPDY/3.1、HTTP2的。

# nghttpx -f *,443 -b localhost,80 /path/to/private.key /path/to/certification.crt

如果没有什么错误,现在网站应该可以用支持http2的浏览器访问了,当然不支持http2的浏览器会自动降级到spdy或者http1.1上。

nghttpx有一堆高级的参数可以使用,比如变成一个daemon进程,还有指定运行的用户和组,还有输出日志的方法格式等等,就不在这里详述了,可以运行nghttpx -h阅读帮助资料。


  1. http://dev.chromium.org/spdy/spdy-whitepaper#TOC-Preliminary-results 

  2. http://blogs.msdn.com/b/ieinternals/archive/2013/09/24/internet-explorer-11-changelist-change-log.aspx 

  3. http://blog.chromium.org/2015/02/hello-http2-goodbye-spdy-http-is_9.html 

  4. http://tatsuhiro-t.github.io/spdylay/