http诞生之初主要是应用于web端内容获取,那时候内容还不像如今这样丰富,排版也没那么精美,用户交互的场景几乎没有。对于这种简单的获取网页内容的场景,http表现得还算不错。但随着互联网的发展和web2.0的诞生,更多的内容开始被展现(更多的图片文件),排版变得更精美(更多的css),更复杂的交互也被引入(更多的js)。用户打开一个网站首页所加载的数据总量和请求的个数也在不断增长。今天绝大部分的门户网站首页大小都会超过2M,请求数量能够多达100个。另外一个普遍的应用是在移动互联网的客户端app,不一样性质的app对http的使用差别很大。对于电商类app,加载首页的请求也可能多达10多个。对于微信这类IM,http请求可能仅限于语音和图片文件的下载,请求出现的频率并不算高。javascript
影响一个网络请求的因素主要有两个,带宽和延迟。今天的网络基础建设已经使得带宽获得极大的提高,大部分时候都是延迟在影响响应速度。http1.0被抱怨最多的就是链接没法复用,和head of line blocking这两个问题。理解这两个问题有一个十分重要的前提:客户端是依据域名来向服务器创建链接,通常PC端浏览器会针对单个域名的server同时创建6~8个链接,手机端的链接数则通常控制在4~6个。显然链接数并非越多越好,资源开销和总体延迟都会随之增大。css
链接没法复用会致使每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。html
head of line blocking会致使带宽没法被充分利用,以及后续健康请求被阻塞。假设有5个请求同时发出,以下图:java
[图1] git
对于http1.0的实现,在第一个请求没有收到回复以前,后续从应用层发出的请求只能排队,请求2,3,4,5只能等请求1的response回来以后才能逐个发出。网络通畅的时候性能影响不大,一旦请求1的request由于什么缘由没有抵达服务器,或者response由于网络阻塞没有及时返回,影响的就是全部后续请求,问题就变得比较严重了。github
http1.0和1.1虽然存在这么多问题,业界也想出了各类优化的手段,但这些方法手段都是在尝试绕开协议自己的缺陷,都有种隔靴搔痒,治标不治本的感受。直到2012年google如一声惊雷提出了SPDY的方案,你们才开始从正面看待和解决老版本http协议自己的问题,这也直接加速了http2.0的诞生。实际上,http2.0是以SPDY为原型进行讨论和标准化的。为了给http2.0让路,google已决定在2016年再也不继续支持SPDY开发,但在http2.0出生以前,SPDY已经有了至关规模的应用,做为一个过渡方案恐怕在还将一段时间内继续存在。如今很多app客户端和server都已经使用了SPDY来提高体验,http2.0在老的设备和系统上还没法使用(iOS系统只有在iOS9+上才支持),因此能够预见将来几年spdy将和http2.0共同服务的状况。web
SPDY的目标在一开始就是瞄准http1.x的痛点,即延迟和安全性。咱们上面通篇都在讨论延迟,至于安全性,因为http是明文协议,其安全性也一直被业界诟病,不过这是另外一个大的话题。若是以下降延迟为目标,应用层的http和传输层的tcp都是都有调整的空间,不过tcp做为更底层协议存在已达数十年之久,其实现已深植全球的网络基础设施当中,若是要动必然伤经动骨,业界响应度必然不高,因此SPDY的手术刀对准的是http。算法
为了增长业界响应的可能性,聪明的google一开始就避开了从传输层动手,并且打算利用开源社区的力量以提升扩散的力度,对于协议使用者来讲,也只须要在请求的header里设置user agent,而后在server端作好支持便可,极大的下降了部署的难度。SPDY的设计以下:chrome
[图6]浏览器
SPDY位于HTTP之下,TCP和SSL之上,这样能够轻松兼容老版本的HTTP协议(将http1.x的内容封装成一种新的frame格式),同时可使用已有的SSL功能。SPDY的功能能够分为基础功能和高级功能两部分,基础功能默认启用,高级功能须要手动启用。
SPDY的成绩能够用google官方的一个数字来讲明:页面加载时间相比于http1.x减小了64%。并且各大浏览器厂商在SPDY诞生以后的1年多里都陆续支持了SPDY,很多大厂app和server端框架也都将SPDY应用到了线上的产品当中。
google的官网也给出了他们本身作的一份测试数据。测试对象是25个访问量排名靠前的网站首页,家用网络%1的丢包率,每一个网站测试10次取平均值。结果以下:
[图7]
不开启ssl的时候提高在 27% - 60%,开启以后为39% - 55%。 这份测试结果有两点值得特别注意:
链接究竟是基于域名来创建,仍是不作区分全部子域名都共享一个链接,这个策略选择上值得商榷。google的测试结果测试了两种方案,看结果彷佛是单一链接性能高于多域名链接方式。之因此出现这种状况是因为网页全部的资源请求并非同一时间发出,后续发出的子域名请求若是能复用以前的tcp链接固然性能更好。实际应用场景下应该也是单链接共享模式表现好。
测试基于两种带宽环境,一慢一快。网速快的环境下对减少延迟的提高更大,单链接模式下能够提高至60%。缘由也比较简单,带宽越大,复用链接的请求完成越快,因为三次握手和慢启动致使的延迟损耗就变得更明显。
出了链接模式和带宽以外,丢包率和RTT也是须要测试的参数。SPDY对header的压缩有80%以上,总体包大小能减小大概40%,发送的包越少,天然受丢包率影响也就越小,因此丢包率大的恶劣环境下SPDY反而更能提高体验。下图是受丢包率影响的测试结果,丢包率超过2.5%以后就没有提高了:
[图8]
RTT越大,延迟会越大,在高RTT的场景下,因为SPDY的request是并发进行的,全部对包的利用率更高,反而能更明显的减少整体延迟。测试结果以下:
[图9]
SPDY从2012年诞生到2016中止维护,时间跨度对于网络协议来讲其实很是之短。若是HTTP2.0没有出来,google或许能收集到更多业界产品的真实反馈和数据,毕竟google本身的测试环境相对简单。但SPDY也完成了本身的使命,做为一向扮演拓荒者角色的google应该也早就预见了这样的结局。SPDY对产品网络体验的提高到底如何,恐怕只有各大厂产品经理才清楚了。
SPDY的诞生和表现说明了两件事情:一是在现有互联网设施基础和http协议普遍使用的前提下,是能够经过修改协议层来优化http1.x的。二是针对http1.x的修改确实效果明显并且业界反馈很好。正是这两点让IETF(Internet Enginerring Task Force)开始正式考虑制定HTTP2.0的计划,最后决定以SPDY/3为蓝图起草HTTP2.0,SPDY的部分设计人员也被邀请参与了HTTP2.0的设计。
HTTP2.0与SPDY的起点不一样,SPDY能够说是google的“玩具”,最先出如今自家的chrome浏览器和server上,好很差玩以及别人会不会跟着一块儿玩对google来讲无关痛痒。但HTTP2.0做为业界标准还没出生就是众人瞩目的焦点,一开始若是有什么瑕疵或者不兼容的问题影响可能又是数十年之久,因此考虑的问题和角度要很是之广。咱们来看下HTTP2.0一些重要的设计前提:
由于客户端和server之间在确立使用http1.x仍是http2.0以前,必需要要确认对方是否支持http2.0,因此这里必需要有个协商的过程。最简单的协商也要有一问一答,客户端问server答,即便这种最简单的方式也多了一个RTT的延迟,咱们之因此要修改http1.x就是为了下降延迟,显然这个RTT咱们是没法接受的。google制定SPDY的时候也遇到了这个问题,他们的办法是强制SPDY走https,在SSL层完成这个协商过程。ssl层的协商在http协议通讯以前,因此是最适合的载体。google为此作了一个tls的拓展,叫NPN(Next Protocol Negotiation),从名字上也能够看出,这个拓展主要目的就是为了协商下一个要使用的协议。HTTP2.0虽然也采用了相同的方式,不过HTTP2.0通过激烈的讨论,最终仍是没有强制HTTP2.0要走ssl层,大部分浏览器厂商(除了IE)却只实现了基于https的2.0协议。HTTP2.0没有使用NPN,而是另外一个tls的拓展叫ALPN(Application Layer Protocol Negotiation)。SPDY也打算从NPN迁移到ALPN了。
各浏览器(除了IE)之因此只实现了基于SSL的HTTP2.0,另外一个缘由是走SSL请求的成功率会更高,被SSL封装的request不会被监听和修改,这样网络中间的网络设备就没法基于http1.x的认知去干涉修改request,http2.0的request若是被意外的修改,请求的成功率天然会降低。
HTTP2.0协议没有强制使用SSL是由于听到了不少的反对声音,毕竟https和http相比,在不优化的前提下性能差了很多,要把https优化到几乎不增长延迟的程度又须要花费很多力气。IETF面对这种两难的处境作了妥协,但大部分浏览器厂商(除了IE)并不买账,他们只认https2.0。对于app开发者来讲,他们能够坚持使用没有ssl的http2.0,不过要承担一个多余的RTT延迟和请求可能被破坏的代价。
HTTP2.0做为新版协议,改动细节必然不少,不过对应用开发者和服务提供商来讲,影响较大的就几点。
http1.x诞生的时候是明文协议,其格式由三部分组成:start line(request line或者status line),header,body。要识别这3部分就要作协议解析,http1.x的解析是基于文本。基于文本协议的格式解析存在自然缺陷,文本的表现形式有多样性,要作到健壮性考虑的场景必然不少,二进制则不一样,只认0和1的组合。基于这种考虑http2.0的协议解析决定采用二进制格式,实现方便且健壮。
有人可能会以为基于文本的http调试方便不少,像firebug,chrome,charles等很多工具均可以即时调试修改请求。实际上如今不少请求都是走https了,要调试https请求必须有私钥才行。http2.0的绝大部分request应该都是走https,因此调试方便没法做为一个有力的考虑因素了。curl,tcpdump,wireshark这些工具会更适合http2.0的调试。
http2.0用binary格式定义了一个一个的frame,和http1.x的格式对好比下图:
[图10]
http2.0的格式定义更接近tcp层的方式,这张二机制的方式十分高效且精简。length定义了整个frame的开始到结束,type定义frame的类型(一共10种),flags用bit位定义一些重要的参数,stream id用做流控制,剩下的payload就是request的正文了。
虽然看上去协议的格式和http1.x彻底不一样了,实际上http2.0并无改变http1.x的语义,只是把原来http1.x的header和body部分用frame从新封装了一层而已。调试的时候浏览器甚至会把http2.0的frame自动还原成http1.x的格式。具体的协议关系能够用下图表示:
[图11]
http2.0要解决的一大难题就是多路复用(MultiPlexing),即链接共享。上面协议解析中提到的stream id就是用做链接共享机制的。一个request对应一个stream并分配一个id,这样一个链接上能够有多个stream,每一个stream的frame能够随机的混杂在一块儿,接收方能够根据stream id将frame再归属到各自不一样的request里面。
前面还提到过链接共享以后,须要优先级和请求依赖的机制配合才能解决关键请求被阻塞的问题。http2.0里的每一个stream均可以设置又优先级(Priority)和依赖(Dependency)。优先级高的stream会被server优先处理和返回给客户端,stream还能够依赖其它的sub streams。优先级和依赖都是能够动态调整的。动态调整在有些场景下颇有用,假想用户在用你的app浏览商品的时候,快速的滑动到了商品列表的底部,但前面的请求先发出,若是不把后面的请求优先级设高,用户当前浏览的图片要到最后才能下载完成,显然体验没有设置优先级好。同理依赖在有些场景下也有妙用。
前面提到过http1.x的header因为cookie和user agent很容易膨胀,并且每次都要重复发送。http2.0使用encoder来减小须要传输的header大小,通信双方各自cache一份header fields表,既避免了重复header的传输,又减少了须要传输的大小。高效的压缩算法能够很大的压缩header,减小发送包的数量从而下降延迟。
这里普及一个小知识点。如今你们都知道tcp有slow start的特性,三次握手以后开始发送tcp segment,第一次能发送的没有被ack的segment数量是由initial tcp window大小决定的。这个initial tcp window根据平台的实现会有差别,但通常是2个segment或者是4k的大小(一个segment大概是1500个字节),也就是说当你发送的包大小超过这个值的时候,要等前面的包被ack以后才能发送后续的包,显然这种状况下延迟更高。intial window也并非越大越好,太大会致使网络节点的阻塞,丢包率就会增长,具体细节能够参考IETF这篇文章。http的header如今膨胀到有可能会超过这个intial window的值了,因此更显得压缩header的重要性。
SPDY/2使用的是gzip压缩算法,但后来出现的两种攻击方式BREACH和CRIME使得即便走ssl的SPDY也能够被破解内容,最后综合考虑采用的是一种叫HPACK的压缩算法。这两个漏洞和相关算法能够点击连接查看更多的细节,不过这种漏洞主要存在于浏览器端,由于须要经过javascript来注入内容并观察payload的变化。
不少app客户端都有取消图片下载的功能场景,对于http1.x来讲,是经过设置tcp segment里的reset flag来通知对端关闭链接的。这种方式会直接断开链接,下次再发请求就必须从新创建链接。http2.0引入RST_STREAM类型的frame,能够在不断开链接的前提下取消某个request的stream,表现更好。
Server Push的功能前面已经提到过,http2.0能经过push的方式将客户端须要的内容预先推送过去,因此也叫“cache push”。另外有一点值得注意的是,客户端若是退出某个业务场景,出于流量或者其它因素须要取消server push,也能够经过发送RST_STREAM类型的frame来作到。
TCP协议经过sliding window的算法来作流量控制。发送方有个sending window,接收方有receive window。http2.0的flow control是相似receive window的作法,数据的接收方经过告知对方本身的flow window大小代表本身还能接收多少数据。只有Data类型的frame才有flow control的功能。对于flow control,若是接收方在flow window为零的状况下依然更多的frame,则会返回block类型的frame,这张场景通常代表http2.0的部署出了问题。
tcp协议优化的一个经典场景是:Nagle算法和Berkeley的delayed ack算法的对立。http2.0并无对tcp层作任何修改,因此这种对立致使的高延迟问题依然存在。要么经过TCP_NODELAY禁用Nagle算法,要么经过TCP_QUICKACK禁用delayed ack算法。貌似http2.0官方建议是设置TCP_NODELAY。
HTTP2.0使用了tls的拓展ALPN来作协议升级,除此以外加密这块还有一个改动,HTTP2.0对tls的安全性作了近一步增强,经过黑名单机制禁用了几百种再也不安全的加密算法,一些加密算法可能还在被继续使用。若是在ssl协商过程中,客户端和server的cipher suite没有交集,直接就会致使协商失败,从而请求失败。在server端部署http2.0的时候要特别注意这一点。
SPDY和HTTP2.0之间的暧昧关系,以及google做为SPDY的创造者,这两点很容易让阴谋论者怀疑google是否会成为协议的最终收益方。这实际上是废话,google固然会受益,任何新协议使用者都会从中受益,至于谁吃肉,谁喝汤看的是本身的本事。从整个协议的变迁史也能够粗略看出,新协议的诞生彻底是针对业界现存问题对症下药,并无google业务相关的痕迹存在,google至始至终只扮演了一个角色:you can you up。
HTTP2.0不会是万金油,但抹了也不会有反作用。HTTP2.0最大的亮点在于多路复用,而多路复用的好处只有在http请求量大的场景下才明显,因此有人会以为只适用于浏览器浏览大型站点的时候。这么说其实没错,但http2.0的好处不只仅是multiplexing,请求压缩,优先级控制,server push等等都是亮点。对于内容型移动端app来讲,好比淘宝app,http请求量大,多路复用仍是能产生明显的体验提高。多路复用对延迟的改变能够参考下这个测试网址。
HTTP2.0对于ssl的依赖使得有些开发者望而生畏。很多开发者对ssl还停留在高延迟,CPU性能损耗,配置麻烦的印象中。其实ssl于http结合对性能的影响已经能够优化到忽略的程度了,网上也有很多文章能够参考。HTTP2.0也能够不走ssl,有些场景确实可能不适合https,好比对代理服务器的cache依赖,对于内容安全性不敏感的get请求能够经过代理服务器缓存来优化体验。
HTTP2.0做为新版本的网络协议确定须要一段时间去普及,但HTTP自己属于应用层协议,和当年的网络层协议IPV6不一样,离底层协议越远,对网络基础硬件设施的影响就越小。HTTP2.0甚至还特地的考虑了与HTTP1.x的兼容问题,只是在HTTP1.x的下面作了一层framing layer,更使得其普及的阻力变小。因此不出意外,HTTP2.0的普及速度可能会远超大部分人的预期。
Firefox 2015年在其浏览器流量中检测到,有13%的http流量已经使用了http2.0,27%的https也是http2.0,并且还处于持续的增加当中。通常用户察觉不到是否使用了http2.0,不过能够装这样一个插件,安装以后若是网站是http2.0的,在地址栏的最右边会有个闪电图标。还可使用这个网站来测试。对于开发者来讲,能够经过Web Developer的Network来查看协议细节,以下图:
[图12]
其中Version:HTTP/2.0已经很明确代表协议类型,Firefox还在header里面插入了X-Firefox-Spdy:“h2”,也能够看出是否使用http2.0。
Chrome在2015年检测到的http2.0流量大概有18%。不过这个数字原本会更高,由于Chrome如今很大一部分流量都在试验QUIC(google正在开辟的另外一块疆土)。Chrome上也可使用相似的插件来判断网站是不是使用http2.0。