全面解析HTTP/2:历史、特性、调试、性能

写在前面

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是互联网上应用最为广泛的一种网络协议。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。通过 HTTP 或者 HTTPS 协议请求的资源由统一资源标识符(URI)来标识。

虽然HTTP/1.1稳定运行了十多年了,但HTTP/2来势汹汹,作为技术工程师有必要学习一下HTTP/2。今天,阿里云CDN安防技术专家金九将从历史、特性、调试、性能四个层面,来全面解析HTTP/2,希望本文可以给你带来一些启发。


一、历史

1、 HTTP/0.9
最早的原型,1991年发布,该版本极其简单,只支持 GET 方法,不支持 MIME 类型和各种 HTTP 首部等等。

2、 HTTP/1.0
1996年发布。HTTP/1.0在HTTP/0.9的基础之上添加很多方法,各种 HTTP 首部,以及对多媒体对象的处理。

除了GET命令,还引入了POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。

任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。

HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。

可以说,HTTP/1.0是对HTTP/0.9做了革命性的改变,但HTTP/1.0依然有一些缺点,其主要缺点是每个TCP连接只能发送一个请求,发送数据完毕后连接就关闭,如果还要请求其他资源,就得再新建一个连接。虽然有些浏览器为了解决这个问题,用了一个非标准的Connection头部,但这个不是标准头部,各个浏览器和服务器实现有可能不一致,因此不是根本解决办法。

3 、HTTP/1.1
1999年正式发布。HTTP/1.1是当前主流的 HTTP 协议。完善了之前 HTTP 设计中的结构性缺陷,明确了语义,添加和删除了一些特性,支持更加复杂的的 Web 应用。

经过了十多年将近20年的发展,这个版本的HTTP协议已经很稳定了,跟HTTP/1.0相比,它新增了很多引人注目的新特性,比如Host协议头、Range分段请求、默认持久连接、压缩、分块传输编码(chunked)、缓存处理等等,至今都大量使用,而且很多软件依赖这些特性。

虽然HTTP/1.1并不像HTTP/1.0对于HTTP/0.9那样的革命性,但是也有很多增强,目前主流浏览器均默认采用HTTP/1.1。

# 4、SPDY
SPDY(发音:speedy)协议由Google开发,主要解决 HTTP/1.1 效率不高的问题,于2009年公开,到2016年初结束使命。因为HTTP/2已经被IETF标准化了,以后各种新版浏览器都会支持HTTP/2,Google认为SPDY已经没有存在的必要了,接下来的使命由HTTP/2去完成。

5、HTTP/2
HTTP/2是最新的HTTP协议,已于2015年5月份正式发布, Chrome、 IE11、Safari以及Firefox 等主流浏览器已经支持 HTTP/2协议。

注意是HTTP/2而不是HTTP/2.0,这是因为IETF(Internet Engineering Task Force,互联网工程任务组)认为HTTP/2已经很成熟了,没有必要再发布子版本了,以后要是有重大改动就直接发布HTTP/3。

其实,HTTP/2的前身是SPDY,甚至它俩的目标、原理和基本实现都差不多。IETF组委会中有很多Google工程师,将SPDY推动成为标准也就不足为奇了。

HTTP/2不仅优化了性能而且兼容了HTTP/1.1的语义,其几大特性与SPDY差不多,与HTTP/1.1有巨大区别,比如它不是文本协议而是二进制协议,而且HTTP头部采用HPACK进行压缩,支持多路复用、服务器推送等等。


二、特性

1、二进制协议

HTTP/2 采用二进制格式传输数据,而非HTTP/1.x的文本格式。消息头和消息体均采用二进制格式,并称之为”帧“(Frame)。Frame二进制基本格式如下(摘自rfc7540#section-4.1):

+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+

之所以说是基本格式,是因为所有HTTP/2 Frame都是由该基本格式来封装,类似于TCP头,目前有10个Frame,由Type字段来区分,各个Frame都有自己的二进制格式,都封装Frame Payload中。

其中有两个重要的Frame:Headers Frame(Type=0x1)和Data Frame(Type=0x0),分别对应HTTP/1.1中的消息头(Header)和消息体(Body),由此可见语义并没有太大变化,而是文本格式变成二进制的Frame。二者的转换和关系如下图(摘自 《High Performance Browser Networking》):

此外,HTTP/2中还有流(Stream)和消息(Message)的概念,通过Stream Identifier(即流ID)字段来标识,流ID一样的是同一个流,流中包含消息,这个消息对应HTTP/1.x的请求消息(Request Message)或者响应消息(Response Message),消息是通过帧(Frame)来传输的,响应消息比较大,可能由多个Data Frame来传输。HTTP/2中流、消息和帧的对应关系如下图(摘自 《High Performance Browser Networking》):

2、头部压缩

HTTP/1.x 每次请求和响应,都会携带大量冗余消息头信息,比如Cookie和User Agent,基本一样的内容,每次请求浏览器都会默认携带,这会浪费很多带宽资源,也影响了速度。这是因为HTTP是无状态协议,每次请求都必须附上所有信息,从而导致了每次请求都带上大量重复的消息头。

为此,HTTP/2做了优化,对消息头采用HPACK格式进行压缩传输,并对消息头建立索引表,相同的消息头只发送索引号,从而提高效率和速度。但付出的代价是客户端和服务器均维护一个索引表,在如今内存不值钱的时代,这点空间换取时间还是非常值得的。

关于HPACK请参考RFC7541

3、多路复用

多路复用是指在一个TCP连接里,客户端和服务器都可以同时发送多个请求或者响应,对HTTP/1.x来说各个请求和响应都是有严格的次序要求,而在HTTP/2中,不用按照次序一一对应,而且并发的多个请求或者响应中任何一个请求阻塞了不会影响其他的请求或者响应,这样就避免了“队头堵塞”。如下图(摘自 《High Performance Browser Networking》):

4、服务器推送

服务器推送(Server Push)是指在HTTP/2中服务器未经请求可以主动给客户端推送资源。例如服务端可以主动把 图片、JS 和 CSS 文件推送给浏览器,而不需要浏览器解析HTML后再发送这些请求。当浏览器解析HTML后这些需要的资源都已经在浏览器里了,大大提高了网页加载的速度。如下图(摘自 《High Performance Browser Networking》):

浏览器发起请求page.html这个页面,这个页面中引用了script.js和style.css,服务器在响应page.html后顺便推送了script.js和style.css这两个文件,这样浏览器解析完page.html后发现引用的script.js和style.css已经在本地了,不需要再发送请求了,这样就节省了两次请求和这两次请求所花的网络时间,大大提高了网络性能和用户体验。

5、安全

HTTP的安全是由SSL/TLS来保障,也就是HTTPS,其实HTTP/2并不强制要求依赖SSL/TLS,但是,当前主流浏览器均只支持基于SSL/TLS的HTTP/2,况且在网络劫持日益猖獗的互联网环境下,HTTPS将是未来的趋势,HTTP/2基于HTTPS也是未来的趋势,而各大主流浏览器在实现HTTP/2之初均只支持SSL/TLS的HTTP/2,可见安全也是HTTP/2的重要特性之一。


三、调试

从原理和目标上看HTTP/2和SPDY差不多,从Nginx官方代码上看HTTP/2和SPDY的实现也差不多。Nginx官方代码中已经删除了spdy模块的代码,取而代之的是http2模块(ngx_http_v2_module)。

1、启用

1.1、在编译参数中加入http2模块(默认已经有ssl模块了):

# git clone https://github.com/alibaba/tengine.git
# cd tengine
# ./configure --prefix=/opt/tengine --with-http_v2_module
# make
# make install

1.2、生成测试证书和私钥

# cd /etc/pki/CA/
# touch index.txt serial
# echo 01 > serial
# openssl genrsa -out private/cakey.pem 2048
# openssl req -new -x509 -key private/cakey.pem -out cacert.pem
# cd /opt/tengine/conf
# openssl genrsa -out tengine.key 2048
# openssl req -new -key tengine.key -out tengine.csr
...
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:ZJ
Locality Name (eg, city) []:HZ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Aliyun
Organizational Unit Name (eg, section) []:CDN
Common Name (e.g. server FQDN or YOUR name) []:www.tengine.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
...
# openssl x509 -req -in tengine.csr -CA /etc/pki/CA/cacert.pem -CAkey /etc/pki/CA/private/cakey.pem -CAcreateserial -out tengine.crt

1.3、配置http2

server {
        listen       443 ssl http2;
        server_name  www.tengine.com;
        default_type  text/plain;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_certificate     tengine.crt;
        ssl_certificate_key tengine.key;
        ssl_ciphers         EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:EECDH+AES256:EECDH+3DES:RSA+3DESi:RC4-SHA:ALL:!MD5:!aNULL:!EXP:!LOW:!SSLV2:!NULL:!ECDHE-RSA-AES128-GCM-SHA256;
        ssl_prefer_server_ciphers  on;

        location / {
            return 200 "http2 is ok";
        }
    }

1.4、启动tengine即可:

# /opt/tengine/sbin/nginx -c /opt/tengine/conf/nginx.conf
1.5、测试
先绑定/etc/hosts:
127.0.0.1 www.tengine.com
用nghttp工具测试:
jinjiu@j9mac ~/work/pcap$ nghttp 'https://www.tengine.com/' -v
[  0.019] Connected
[  0.043][NPN] server offers:
          * h2
          * http/1.1
The negotiated protocol: h2
[  0.064] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):2147483647]
          [SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[  0.064] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=2147418112)
[  0.064] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.064] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.064] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.064] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.077] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.077] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.077] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.077] send HEADERS frame <length=39, flags=0x25, stream_id=13>
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: https
          :authority: www.tengine.com
          accept: /
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.9.2
[  0.087] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.087] recv (stream_id=13) :status: 200
[  0.087] recv (stream_id=13) server: Tengine/2.2.0
[  0.087] recv (stream_id=13) date: Mon, 26 Sep 2016 03:00:01 GMT
[  0.087] recv (stream_id=13) content-type: text/plain
[  0.087] recv (stream_id=13) content-length: 11
[  0.087] recv HEADERS frame <length=63, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
http2 is ok[  0.087] recv DATA frame <length=11, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.087] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

用chrome浏览器测试:

2、抓包分析

从抓包来学习HTTP/2格式是最好的办法,但HTTP/2又是基于https的,也就是加密的,直接抓包看到的是密文,没有意义,还好Wireshark提供解密https流量的办法可以比较方便地调试HTTP/2。

2.1、先导出系统变量$SSLKEYLOGFILE,以OSX系统为例

#bash
echo "\nexport SSLKEYLOGFILE=~/ssl_debug/ssl_pms.log" >> ~/.bash_profile && . ~/.bash_profile

2.2、打开Chrome或者Firefox

#chrome
open /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
#firefox
open /Applications/Firefox.app/Contents/MacOS/firefox

用打开的Chrome或者Firefox浏览器打开https网站,比如:https://www.taobao.com
然后看看文件~/ssl_debug/ssl_pms.log有没有内容,有内容就可以用Wireshark解密https数据了。

2.3、Wireshark设置

Wireshark->Perferences...->Protocols->SSL

启动抓包:

可见已经能得到HTTP/2的明文数据了。篇幅所限,在此不展开具体细节了,更多HTTP/2二进制协议细节请参考RFC7540


四、性能

测试机器配置:cache1.cn1, 115.238.23.13, 16核Intel(R) Xeon(R) CPU L5630 @ 2.13GHz,48G内存,万兆网卡
测试工具:h2load
测试结果:


结论

1、无论是否keepalive,HTTP/2与SPDY/3.1性能相当,HTTP/2略优。
2、在size为1k、2k、4k测试结果中保持较低RT情况下QPS也较高,CPU没有达到瓶颈,加大压测客户端数量后QPS有所提高,但RT变大,5xx也变多(这部分数据没有给出,是测试时记录的现象)。
在size为16k、32k、64k、128k、256k的测试结果中CPU达到瓶颈,随着size变大,QPS降低,RT变高,CPU性能消耗较多的函数是gcm_ghash_clmul。
在size为512k时网卡达到瓶颈,CPU没有达到瓶颈。
3、在开启keepalive的情况下,HTTP/1.1的性能与HTTP/2的性能差距不是很大。但关闭keepalive时HTTP/2的性能比HTTP/1.1更好。


写在最后

相比之前的传输协议,HTTP/2在底层方面做了很多优化。有安全、省时、简化开发、更好的适应复杂页面、提供缓存利用率等显著的优势,各大公司也已经纷纷开始使用HTTP/2协议了。阿里云早在去年发布的CDN6.0服务就已正式支持HTTP/2,访问速度最高可提升68%。不知不觉中,一个更安全、更可靠、更高速的时代已经悄然而至。

时间: 2024-09-13 01:00:47

全面解析HTTP/2:历史、特性、调试、性能的相关文章

研究 Java 中 XML 文档模型的特性和性能

xml|性能 Java 中的 XML: 文档模型,第一部分:性能 研究 Java 中 XML 文档模型的特性和性能 文档选项 将此页作为电子邮件发送 最新推荐 Java 应用开发源动力 - 下载免费软件,快速启动开发 级别: 初级 Dennis M. Sosnoski, 总裁, Sosnoski Software Solutions, Inc. 2001 年 9 月 01 日 在本文中,Java 顾问 Dennis Sosnoski 比较几个 Java 文档模型的性能和功能.当选择模型时,无法做

《PostgreSQL 9.0性能调校》一一1.1 PostgreSQL历史版本的性能

1.1 PostgreSQL历史版本的性能 2005年11月,PostgreSQL发布了其8.1版本.该版本中包含了众多内部结构的改进,其中一些期望能够提高多个活动客户端在多处理器系统下的数据库性能.其结果是在处理沉重工作负荷时,数据库能力得到成比例的提高.在如今的硬件设备上进行的基准评测,突出地显示了其对先前版本的跨越.可以在http://suckit.blog.hu/2009/09/29/postgresql_history看到György Vilmos 对8.0版至8.4版的性能横向比较.

Angular2 VS Angular4 深度对比:特性、性能

在Web应用开发领域,Angular被认为是最好的开源JavaScript框架之一. Google的Angular团队已于3月23日发布了Angular4,而期待已久的Angular2版本则是之前版本的完全重构. 对于成熟的开发人员来说,有以上两种选择是一件很棒的事情:但是,对于处于学习阶段的新晋开发人员来说,可能有点不知如何选择. 那么,本文将会对Angular2和Angular4进行深度对比,以便帮助大家更好的了解这两个版本.   Angular2 Angular2是在2015年底发布的.接

C++ 语言特性的性能分析

大多数开发人员通常都有这个观点,即汇编语言和 C 语言适合用来编写对性能要求非常高的程序.而 C++ 语言的主要应用范围是编写复杂度非常高的程序,但是对性能要求不是那么严格的程序.但是事实往往并非如此,很多时候,一个程序的速度在框架设计完成时大致已经确定了,而并非是因为采用了C++语言才使其速度没有达到预期的目标.因此当一个程序的性能需要提高时,首先需要做的是用性能检测工具对其运行的时间分布进行一个准确的测量,找出关键路径和真正的瓶颈所在,然后针对瓶颈进行分析和优化,而不是一味盲目地将性能低劣归

解析css文件:谷歌插件调试和火狐插件调试

文章简介:如需调试功能,请在编译输出的时候输出debug信息,那样解析的css文件中就会包含debug信息,然后通过firebug或谷歌的调试工具就可以定位到我们编辑的scss文件,而不是解析后的css文件. 如需调试功能,请在编译输出的时候输出debug信息,那样解析的css文件中就会包含debug信息,然后通过firebug或谷歌的调试工具就可以定位到我们编辑的scss文件,而不是解析后的css文件. 如果你的css文件中没有以@media -sass-debug-info开头的代码,说明没

CLR全面透彻解析: 及早并经常评量性能,第2部分

在上期的"CLR 全面透彻解析"中,我强调要可靠地创建高性能的程序,您需要了解设计 初期所使用的各个组件的性能(msdn2.microsoft.com/magazine/cc424899).这就需要用到性能数据. 因此,测量是设计过程中不可或缺的一部分. 我还在那一期中介绍了一款名为 MeasureIt 的工具,利用它可以轻松地创建新基准,从而快速地收集 制定良好设计决策所需的数据.诸如 MeasureIt 之类的工具所提供的原始数字是极为重要的,另外,使 人们能够了解这些数字的基本含

Firefox 将启用 W^X 安全特性:性能影响不大

Mozilla开发者们已经为Firefox浏览器加入了OpenBSD的W^X安全特性,以应对基本的缓冲区溢出和内存泄露问题."W^X" 是"写异或执行"(Write XOR Execute)的缩写,作为OpenBSD中富有代表性的安全特性,Mozilla的开发者们已经将它移植到了Firefox的JIT代码编译器中. Jan de Mooij指出,该功能在Firefox底层运行,并且影响在浏览器内执行的代码是如何与系统内存交互. W^R内存保护机制的原则是,一个进程(

Node.js V0.12新特性:性能优化

v0.12悠长的开发周期(已经过去九个月了,并且还在继续,是有史以来最长的一次)让核心团队和贡献者们有充分的机会对性能做一些优化.本文会介绍其中最值得注意的几个. 支持塞住模式的可写流 现在可写流可以支持"塞住(corked)"模式,类似于你执行man tcp时见到的socket选项TCP_CORK和TCP_NOPUSH. 当被塞住时,写到流中的数据会排队直到流被重新开塞(uncorked).这样Node.js可以将比较小的写操作合并成比较大的,从而减少系统调用和TCP往返. http

CLR全面透彻解析: 及早并经常评量性能,第1部分

作为 Microsoft .NET Framework 公共语言运行库团队的性能架构师,帮助大家充分利用运行时 编写高性能的应用程序是我的职责所在.这无论是在 .NET 还是在其他语言中都不神秘--您 只需要在设计之初即考虑应用程序的性能问题即可.有很多应用程序在编写时根本未考虑性能问题.这通 常无关紧要,因为大多数程序的计算量相对较少,而且同和它们交互的人类相比,程序的计算速度要快得 多.遗憾的是,当真的需要程序具有较高性能时,我们却缺乏相关的知识.技能和工具来很好地实现这一 点. 在这里我将

解析phpstorm + xdebug 远程断点调试_php技巧

XDEBUG配置:1.安装 xdebug 略了.网上有很多资料. 重点写php.ini的配置 [XDebug] 复制代码 代码如下: zend_extension="/usr/lib/php5/20090626+lfs/xdebug.so"xdebug.default_enable = Onxdebug.collect_params = Onxdebug.remote_connect_back = On           //如果开启此,将忽略下面的 xdebug.remote_ho