再谈HTTP2性能提高之背后原理—HTTP2历史解剖

即便千辛万苦,仍是把网站升级到http2了,遇坑如《phpcms v9站http升级到https加http2遇到到坑》。javascript

由于理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 彻底语义,进一步减小了网络延迟。php

对于前端开发人员来讲,无疑减小了在前端方面的优化工做。好比雪碧图&文件合并||内容内嵌||域名分片html

http1.0的缺点

http1.0被抱怨最多的就是链接没法复用,和head of line blocking这两个问题。理解这两个问题有一个十分重要的前提:客户端是依据域名来向服务器创建链接,通常PC端浏览器会针对单个域名的server同时创建6~8个链接,手机端的链接数则通常控制在4~6个。显然链接数并非越多越好,资源开销和总体延迟都会随之增大。前端

链接没法复用会致使每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。java

head of line blocking会致使带宽没法被充分利用,以及后续健康请求被阻塞。假设有5个请求同时发出,对于http1.0的实现,在第一个请求没有收到回复以前,后续从应用层发出的请求只能排队,请求2,3,4,5只能等请求1的response回来以后才能逐个发出。一旦请求1的request由于什么缘由没有抵达服务器,或者response由于网络阻塞没有及时返回,影响的就是全部后续请求,问题就变得比较严重了。nginx

对于http1.0的缺点优化方案

解决链接没法复用

http1.0协议头里能够设置Connection:Keep-Alive。在header里设置Keep-Alive能够在必定时间内复用链接,具体复用时间的长短能够由服务器控制,通常在15s左右。到http1.1以后Connection的默认值就是Keep-Alive,若是要关闭链接复用须要显式的设置Connection:Close。一段时间内的链接复用对PC端浏览器的体验帮助很大,由于大部分的请求在集中在一小段时间之内。但对移动app来讲,成效不大,app端的请求比较分散且时间跨度相对较大。因此移动端app通常会从应用层寻求其它解决方案,长链接方案或者伪长链接方案:git

方案一:基于tcp的长连接

如今愈来愈多的移动端app都会创建一条本身的长连接通道,通道的实现是基于tcp协议。基于tcp的socket编程技术难度相对复杂不少,并且须要本身制定协议,但带来的回报也很大。信息的上报和推送变得更及时,在请求量爆发的时间点还能减轻服务器压力(http短链接模式会频繁的建立和销毁链接)。不止是IM app有这样的通道,像淘宝这类电商类app都有本身的专属长链接通道了。如今业界也有很多成熟的方案可供选择了,google的protobuf就是其中之一。github

方案二:http long-polling

客户端在初始状态就会发送一个polling请求到服务器,服务器并不会立刻返回业务数据,而是等待有新的业务数据产生的时候再返回。因此链接会一直被保持,一旦结束立刻又会发起一个新的polling请求,如此反复,因此一直会有一个链接被保持。服务器有新的内容产生的时候,并不须要等待客户端创建一个新的链接。作法虽然简单,但有些难题须要攻克才能实现稳定可靠的业务框架:web

  • 和传统的http短连接相比,长链接会在用户增加的时候极大的增长服务器压力算法

  • 移动端网络环境复杂,像wifi和4g的网络切换,进电梯致使网络临时断掉等,这些场景都须要考虑怎么重建健康的链接通道。

  • 这种polling的方式稳定性并很差,须要作好数据可靠性的保证,好比重发和ack机制。

  • polling的response有可能会被中间代理cache住,要处理好业务数据的过时机制。

http long-polling

long-polling方式还有一些缺点是没法克服的,好比每次新的请求都会带上重复的header信息,还有数据通道是单向的,主动权掌握在server这边,客户端有新的业务请求的时候没法及时传送。

方案三:http streaming

同long-polling不一样的是,server并不会结束初始的streaming请求,而是持续的经过这个通道返回最新的业务数据。显然这个数据通道也是单向的。streaming是经过在server response的头部里增长”Transfer Encoding: chunked”来告诉客户端后续还会有新的数据到来。除了和long-polling相同的难点以外,streaming还有几个缺陷:

  • 有些代理服务器会等待服务器的response结束以后才会将结果推送到请求客户端。对于streaming这种永远不会结束的方式来讲,客户端就会一直处于等待response的过程当中。

  • 业务数据没法按照请求来作分割,因此客户端没收到一块数据都须要本身作协议解析,也就是说要作本身的协议定制。

streaming不会产生重复的header数据。

方案四:web socket

WebSocket和传统的tcp socket链接类似,也是基于tcp协议,提供双向的数据通道。WebSocket优点在于提供了message的概念,比基于字节流的tcp socket使用更简单,同时又提供了传统的http所缺乏的长链接功能。不过WebSocket相对较新,2010年才起草,并非全部的浏览器都提供了支持。各大浏览器厂商最新的版本都提供了支持。

解决head of line blocking

Head of line blocking(如下简称为holb)是http2.0以前网络体验的最大祸源。健康的请求会被不健康的请求影响,并且这种体验的损耗受网络环境影响,出现随机且难以监控。为了解决holb带来的延迟,协议设计者设计了一种新的pipelining机制。

20160911174859654955.png

不过pipelining并非救世主,它也存在很多缺陷:

  • pipelining只能适用于http1.1,通常来讲,支持http1.1的server都要求支持pipelining。

  • 只有幂等的请求(GET,HEAD)能使用pipelining,非幂等请求好比POST不能使用,由于请求之间可能会存在前后依赖关系。

  • head of line blocking并无彻底获得解决,server的response仍是要求依次返回,遵循FIFO(first in first out)原则。也就是说若是请求1的response没有回来,2,3,4,5的response也不会被送回来。

  • 绝大部分的http代理服务器不支持pipelining。

  • 和不支持pipelining的老服务器协商有问题。

  • 可能会致使新的Front of queue blocking问题。

正是由于有这么多的问题,各大浏览器厂商要么是根本就不支持pipelining,要么就是默认关掉了pipelining机制,并且启用的条件十分苛刻。能够参考chrome对于pipeling的问题描述

HTTP2的优点

二进制分帧

http1.x诞生的时候是明文协议,其格式由三部分组成:start line(request line或者status line),header,body。要识别这3部分就要作协议解析,http1.x的解析是基于文本。基于文本协议的格式解析存在自然缺陷,文本的表现形式有多样性,要作到健壮性考虑的场景必然不少,二进制则不一样,只认0和1的组合。基于这种考虑http2.0的协议解析决定采用二进制格式,实现方便且健壮。

http2.0用binary格式定义了一个一个的frame,和http1.x的格式对好比下图:

 

20160911174955780620.png

http2.0的格式定义更接近tcp层的方式,这张二机制的方式十分高效且精简。

  • length定义了整个frame的开始到结束

  • type定义frame的类型(一共10种)

  • flags用bit位定义一些重要的参数

  • stream id用做流控制

  • payload就是request的正文了

为何么能在不改动 HTTP/1.x 的语义、方法、状态码、URI 以及首部字段….. 的状况下, HTTP/2 是如何作到「突破 HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量」?

关键之一就是在 应用层(HTTP/2)和传输层(TCP or UDP)之间增长一个二进制分帧层。


在二进制分帧层中, HTTP/2 会将全部传输的信息分割为更小的消息和帧(frame),并对它们采用二进制格式的编码 ,其中 HTTP1.x 的首部信息会被封装到 HEADER frame,而相应的 Request Body 则封装到 DATA frame 里面。
HTTP/2 通讯都在一个链接上完成,这个链接能够承载任意数量的双向数据流。

在过去, HTTP 性能优化的关键并不在于高带宽,而是低延迟。TCP 链接会随着时间进行自我「调谐」,起初会限制链接的最大速度,若是数据成功传输,会随着时间的推移提升传输的速度。这种调谐则被称为 TCP 慢启动。具体复习:《再深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP》、《从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造

因为这种缘由,让本来就具备突发性和短时性的 HTTP 链接变的十分低效。

HTTP/2 经过让全部数据流共用同一个链接,能够更有效地使用 TCP 链接,让高带宽也能真正的服务于 HTTP 的性能提高。
总结:

  • 单链接多资源的方式,减小服务端的连接压力,内存占用更少,链接吞吐量更大

  • 因为 TCP 链接的减小而使网络拥塞情况得以改善,同时慢启动时间的减小,使拥塞和丢包恢复速度更快

多路复用 (Multiplexing)||链接共享

多路复用容许同时经过单一的 HTTP/2 链接发起多重的请求-响应消息。

众所周知 ,在 HTTP/1.1 协议中 「浏览器客户端在同一时间,针对同一域名下的请求有必定数量限制。超过限制数目的请求会被阻塞」。

Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.

source:RFC-2616-8.1.4 Practical Considerations

好比TCP创建链接时三次握手有1.5个RTT(round-trip time)的延迟,为了不每次请求的都经历握手带来的延迟,应用层会选择不一样策略的http长连接方案。又好比TCP在创建链接的初期有慢启动(slow start)的特性,因此链接的重用老是比新建链接性能要好

下图总结了不一样浏览器对该限制的数目。

来源:Roundup on Parallel Connections 

这也是为什么一些站点会有多个静态资源 CDN 域名的缘由之一

上面协议解析中提到的stream id就是用做链接共享机制的:

一个request对应一个stream并分配一个id,这样一个链接上能够有多个stream,每一个stream的frame能够随机的混杂在一块儿,接收方能够根据stream id将frame再归属到各自不一样的request里面。于是 HTTP/2 能多路复用(Multiplexing) ,容许同时经过单一的 HTTP/2 链接发起多重的请求-响应消息。

 

 

所以 HTTP/2 能够很容易的去实现多流并行而不用依赖创建多个 TCP 链接,HTTP/2 把 HTTP 协议通讯的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。并行地在同一个 TCP 链接上双向交换消息。

前面还提到过链接共享以后,须要优先级和请求依赖的机制配合才能解决关键请求被阻塞的问题。http2.0里的每一个stream均可以设置又优先级(Priority)和依赖(Dependency)。优先级高的stream会被server优先处理和返回给客户端,stream还能够依赖其它的sub streams。优先级和依赖都是能够动态调整的。动态调整在有些场景下颇有用,假想用户在用你的app浏览商品的时候,快速的滑动到了商品列表的底部,但前面的请求先发出,若是不把后面的请求优先级设高,用户当前浏览的图片要到最后才能下载完成,显然体验没有设置优先级好。同理依赖在有些场景下也有妙用。

首部压缩(Header Compression)

http1.x的header因为cookie和user agent很容易膨胀,并且每次都要重复发送。

HTTP/1.1并不支持 HTTP 首部压缩,为此 SPDY 和 HTTP/2 应运而生

这里普及一个小知识点。如今你们都知道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的变化。

如今SPDY 使用的是通用的DEFLATE 算法,而 HTTP/2 则使用了专门为首部压缩而设计的 HPACK 算法。

http2.0使用encoder来减小须要传输的header大小,通信双方各自cache一份header fields表,既避免了重复header的传输,又减少了须要传输的大小。高效的压缩算法能够很大的压缩header,减小发送包的数量从而下降延迟。


服务端推送(Server Push)

服务端推送是一种在客户端请求以前发送数据的机制。在 HTTP/2 中,服务器能够对客户端的一个请求发送多个响应。Server Push 让 HTTP1.x 时代使用内嵌资源的优化手段变得没有意义;若是一个请求是由你的主页发起的,服务器极可能会响应主页内容、logo 以及样式表,由于它知道客户端会用到这些东西。这至关于在一个 HTML 文档内集合了全部的资源,不过与之相比,服务器推送还有一个很大的优点:能够缓存!也让在遵循同源的状况下,不一样页面之间能够共享缓存资源成为可能。

http2.0引入RST_STREAM类型的frame,能够在不断开链接的前提下取消某个request的stream,表现更好。

重置链接表现更好

不少app客户端都有取消图片下载的功能场景,对于http1.x来讲,是经过设置tcp segment里的reset flag来通知对端关闭链接的。这种方式会直接断开链接,下次再发请求就必须从新创建链接。http2.0引入RST_STREAM类型的frame,能够在不断开链接的前提下取消某个request的stream,表现更好。

流量控制(Flow Control)

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的部署出了问题。

更安全的SSL

HTTP2.0使用了tls的拓展ALPN来作协议升级,除此以外加密这块还有一个改动,HTTP2.0对tls的安全性作了近一步增强,经过黑名单机制禁用了几百种再也不安全的加密算法,一些加密算法可能还在被继续使用。若是在ssl协商过程中,客户端和server的cipher suite没有交集,直接就会致使协商失败,从而请求失败。在server端部署http2.0的时候要特别注意这一点。

 

关于 HTTP/2 的 Server Push 以及 HTTP/2 的缓存策略

典型问题:

「若是客户端早已在缓存中有了一份 copy 怎么办?」还要 Push 吗?

详情参考另外一个答案:

HTTP/2 对如今的网页访问,有什么大的优化呢?体如今什么地方

PS:
强烈推荐阅读  Mark Nottingham 在 Velocity Beijing 2015 的 speech:HTTP/2 for Front-End Developers ,关于 HTTP/2 下的前端性能优化相关。
Slide 地址:HTTP/2 for Front-End Developers

 

按照OSI网络分层模型,IP是网络层协议,TCP是传输层协议,而HTTP是应用层的协议。在这三者之间,SPDY和WebSocket都是与HTTP相关的协议,而TCP是HTTP底层的协议。

HTTP2的发展历史

1、http

HTTP协议通过多年的使用,发现了一些不足,主要是性能方面的,包括:

  • HTTP的链接问题,HTTP客户端和服务器之间的交互是采用请求/应答模式,在客户端请求时,会创建一个HTTP链接,而后发送请求消息,服务端给出应答消息,而后链接就关闭了。(后来的HTTP1.1支持持久链接)

  • 由于TCP链接的创建过程是有开销的,若是使用了SSL/TLS开销就更大。

  • 在浏览器里,一个网页包含许多资源,包括HTML,CSS,JavaScript,图片等等,这样在加载一个网页时要同时打开链接到同一服务器的多个链接。

  • HTTP消息头问题,如今的客户端会发送大量的HTTP消息头,因为一个网页可能须要50-100个请求,就会有至关大的消息头的数据量。

  • HTTP通讯方式问题,HTTP的请求/应答方式的会话都是客户端发起的,缺少服务器通知客户端的机制,在须要通知的场景,如聊天室,游戏,客户端应用须要不断地轮询服务器。


而SPDY和WebSocket是从不一样的角度来解决这些不足中的一部分。除了这两个技术,还有其余技术也在针对这些不足提出改进。

2、SPDY

SPDY的主要目的是减小50%以上的页面加载时间,可是呢不增长部署的复杂性,不影响客户端和服务端的Web应用,只须要浏览器和Web服务器支持SPDY。主要有如下几:

  • 多路复用,一个TCP链接上同时跑多个HTTP请求。请求可设定优先级。

  • 去除不须要的HTTP头,压缩HTTP头,以减小须要的网络带宽。

  • 使用了SSL做为传输协议提供数据安全。

  • 对传输的数据使用gzip进行压缩

  • 提供服务方发起通讯,并向客户端推送数据的机制。

实质上,SPDY就是想不影响HTTP语义的状况下,替换HTTP底层传输的协议来加快页面加载时间。

SPDY的解决办法就是设计了一个会话层协议--帧协议,解决多路复用,优先级等问题,而后在其上实现了HTTP的语义。

SPDY的诞生和表现说明了两件事情:一是在现有互联网设施基础和http协议普遍使用的前提下,是能够经过修改协议层来优化http1.x的。二是针对http1.x的修改确实效果明显并且业界反馈很好。正是这两点让IETF(Internet Enginerring Task Force)开始正式考虑制定HTTP2.0的计划,最后决定以SPDY/3为蓝图起草HTTP2.0,SPDY的部分设计人员也被邀请参与了HTTP2.0的设计。

3、WebSocket

WebSocket则提供使用一个TCP链接进行双向通信的机制,包括网络协议和API,以取代网页和服务器采用HTTP轮询进行双向通信的机制。


本质上来讲,WebSocket是不限于HTTP协议的,可是因为现存大量的HTTP基础设施,代理,过滤,身份认证等等,WebSocket借用HTTP和HTTPS的端口。

因为使用HTTP的端口,所以TCP链接创建后的握手消息是基于HTTP的,由服务器判断这是一个HTTP协议,仍是WebSocket协议。 WebSocket链接除了创建和关闭时的握手,数据传输和HTTP没丁点关系了。
WebSocket也有本身一套帧协议。

4、SPDY和WebSocket的关系

SPDY和WebSocket的关系比较复杂。

  1. 补充关系,两者侧重点不一样。SPDY更侧重于给Web页面的加载提速,而WebSocket更强调为Web应用提供一种双向的通信机制以及API。

  2. 竞争关系,两者解决的问题有交集,好比在服务器推送上SPDY和WebSocket都提供了方案。

  3. 承载关系,试想,若是SPDY的标准化早于WebSocket,WebSocket彻底能够侧重于API,利用SPDY的帧机制和多路复用机制实现该API。 Google提出草案,说WebSocket能够跑在SPDY之上。WebSocket的链接创建在SPDY的流之上,将WebSocket的帧映射到SPDY的帧上。

  4. 融合关系,如微软在HTTP Speed+Mobility中所作的。


http2的竞争兄弟

1. HTTP Speed+Mobility

还有一个有趣的技术叫作HTTP Speed+Mobility,和SPDY同样都是HTTP 2.0标准的竞争者,HTTP Speed+Mobility来自微软。HTTP SM借鉴了SPDY和WebSocket的协议,将两者揉为一体,又有所取舍。


HTTP SM的设计原则包括:
  • 保留HTTP的语义,这一点和SPDY一致,但也正应如此,抛弃了SPDY里的ServerPush。

  • 遵照分层的网络架构,TCP能作的,HTTP SM不作,所以去除了SPDY的流控。

  • 使用现有标准,所以使用HTTP/1.1 Upgrade header机制,借用了WebSocket的握手机制和帧格式(RFC6455)。

  • 客户端掌握内容的控制,所以不强制使用压缩和SSL/TLS。

  • 考虑到网络的费用和电力,这点考虑到了移动设备以及物联网,提供了Credit Control机制。


HTTP SM分如下几层:
  • 会话层和帧协议,这部分取自WebSocket协议。包括握手机制,以及帧格式。

  • 流层(包括多路复用),这部分主要借鉴SPDY,包括多路复用,流优先级,但增长了Credit Control。这部分做为 WebSocket协议的扩展。

  • HTTP层,在流层上实现HTTP语义,这部分也借鉴自SPDY。

2.  Network-Friendly HTTP

NF是HTTP 2.0候选方案之一,主要提出如下改进:

  • 对HTTP头的名称进行二进制编码

  • 对通用HTTP头进行分组

  • 请求/应答的多路复用

  • 分层模型

NF一样定义了帧和流,

 

3. WAKA

WAKA也是HTTP 2.0候选方案之一,是HTTP协议原做者Roy Fielding提出的一个提案。

WAKA支持多路复用,支持优先级。WAKA提出了两个新的HTTP方法,RENDER和MONITOR。

参考资料:

本文主要内容来源:《HTTP 2.0的那些事

文章由本人精炼而成,原文:再谈HTTP2性能提高之背后原理-HTTP2历史解剖 - Network - 周陆军的我的网站

相关文章
相关标签/搜索