即使千辛万苦,还是把网站升级到http2了,遇坑如《phpcms v9站http升级到https加http2遇到到坑》。
因为理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 完全语义,进一步减少了网络延迟。
对于前端开发人员来说,无疑减少了在前端方面的优化工作。比如雪碧图&文件合并||内容内嵌||域名分片
http1.0的缺点
http1.0被抱怨最多的就是连接无法复用,和head of line blocking这两个问题。理解这两个问题有一个十分重要的前提:客户端是依据域名来向服务器建立连接,一般PC端浏览器会针对单个域名的server同时建立6~8个连接,手机端的连接数则一般控制在4~6个。显然连接数并不是越多越好,资源开销和整体延迟都会随之增大。
连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。
head of line blocking会导致带宽无法被充分利用,以及后续健康请求被阻塞。假设有5个请求同时发出,对于http1.0的实现,在第一个请求没有收到回复之前,后续从应用层发出的请求只能排队,请求2,3,4,5只能等请求1的response回来之后才能逐个发出。一旦请求1的request因为什么原因没有抵达服务器,或者response因为网络阻塞没有及时返回,影响的就是所有后续请求,问题就变得比较严重了。
对于http1.0的缺点优化方案
解决连接无法复用
http1.0协议头里可以设置Connection:Keep-Alive。在header里设置Keep-Alive可以在一定时间内复用连接,具体复用时间的长短可以由服务器控制,一般在15s左右。到http1.1之后Connection的默认值就是Keep-Alive,如果要关闭连接复用需要显式的设置Connection:Close。一段时间内的连接复用对PC端浏览器的体验帮助很大,因为大部分的请求在集中在一小段时间以内。但对移动app来说,成效不大,app端的请求比较分散且时间跨度相对较大。所以移动端app一般会从应用层寻求其它解决方案,长连接方案或者伪长连接方案:
方案一:基于tcp的长链接
现在越来越多的移动端app都会建立一条自己的长链接通道,通道的实现是基于tcp协议。基于tcp的socket编程技术难度相对复杂很多,而且需要自己制定协议,但带来的回报也很大。信息的上报和推送变得更及时,在请求量爆发的时间点还能减轻服务器压力(http短连接模式会频繁的创建和销毁连接)。不止是IM app有这样的通道,像淘宝这类电商类app都有自己的专属长连接通道了。现在业界也有不少成熟的方案可供选择了,google的protobuf就是其中之一。
方案二:http long-polling
客户端在初始状态就会发送一个polling请求到服务器,服务器并不会马上返回业务数据,而是等待有新的业务数据产生的时候再返回。所以连接会一直被保持,一旦结束马上又会发起一个新的polling请求,如此反复,所以一直会有一个连接被保持。服务器有新的内容产生的时候,并不需要等待客户端建立一个新的连接。做法虽然简单,但有些难题需要攻克才能实现稳定可靠的业务框架:
和传统的http短链接相比,长连接会在用户增长的时候极大的增加服务器压力
移动端网络环境复杂,像wifi和4g的网络切换,进电梯导致网络临时断掉等,这些场景都需要考虑怎么重建健康的连接通道。
这种polling的方式稳定性并不好,需要做好数据可靠性的保证,比如重发和ack机制。
polling的response有可能会被中间代理cache住,要处理好业务数据的过期机制。
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机制。
不过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的格式对比如下图:
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的发展历史
一、http
HTTP协议经过多年的使用,发现了一些不足,主要是性能方面的,包括:
HTTP的连接问题,HTTP客户端和服务器之间的交互是采用请求/应答模式,在客户端请求时,会建立一个HTTP连接,然后发送请求消息,服务端给出应答消息,然后连接就关闭了。(后来的HTTP1.1支持持久连接)
因为TCP连接的建立过程是有开销的,如果使用了SSL/TLS开销就更大。
在浏览器里,一个网页包含许多资源,包括HTML,CSS,JavaScript,图片等等,这样在加载一个网页时要同时打开连接到同一服务器的多个连接。
HTTP消息头问题,现在的客户端会发送大量的HTTP消息头,由于一个网页可能需要50-100个请求,就会有相当大的消息头的数据量。
HTTP通信方式问题,HTTP的请求/应答方式的会话都是客户端发起的,缺乏服务器通知客户端的机制,在需要通知的场景,如聊天室,游戏,客户端应用需要不断地轮询服务器。
而SPDY和WebSocket是从不同的角度来解决这些不足中的一部分。除了这两个技术,还有其他技术也在针对这些不足提出改进。
二、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的设计。
三、WebSocket
WebSocket则提供使用一个TCP连接进行双向通讯的机制,包括网络协议和API,以取代网页和服务器采用HTTP轮询进行双向通讯的机制。
本质上来说,WebSocket是不限于HTTP协议的,但是由于现存大量的HTTP基础设施,代理,过滤,身份认证等等,WebSocket借用HTTP和HTTPS的端口。
由于使用HTTP的端口,因此TCP连接建立后的握手消息是基于HTTP的,由服务器判断这是一个HTTP协议,还是WebSocket协议。 WebSocket连接除了建立和关闭时的握手,数据传输和HTTP没丁点关系了。
WebSocket也有自己一套帧协议。
四、SPDY和WebSocket的关系
SPDY和WebSocket的关系比较复杂。
补充关系,二者侧重点不同。SPDY更侧重于给Web页面的加载提速,而WebSocket更强调为Web应用提供一种双向的通讯机制以及API。
竞争关系,二者解决的问题有交集,比如在服务器推送上SPDY和WebSocket都提供了方案。
承载关系,试想,如果SPDY的标准化早于WebSocket,WebSocket完全可以侧重于API,利用SPDY的帧机制和多路复用机制实现该API。 Google提出草案,说WebSocket可以跑在SPDY之上。WebSocket的连接建立在SPDY的流之上,将WebSocket的帧映射到SPDY的帧上。
融合关系,如微软在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。
参考资料:
Gitbook 《HTTP2 讲解》 by Calvin Zhang and Simon Xia:http2讲解 - GitBook
HTTPS、SPDY 以及 HTTP/2 性能简单对比:A Simple Performance Comparison of HTTPS, SPDY and HTTP/2
HTTP/2 的压缩算法--HPACK(RFC7541):HPACK: Header Compression for HTTP/2
NGINX HTTP/2 白皮书:https://www.nginx.com/wp-content/uploads/2015/09/NGINX_HTTP2_White_Paper_v4.pdf
NGINX Blog--提升 HTTP/2 性能的 7个小建议:
7 Tips for Faster HTTP/2 Performance(原文)
[译]使用HTTP/2提升性能的7个建议(李松峰译)
O'Reilly HTTP2-high-perf-browser-networking:http://www.oreilly.com/webops-perf/free/files/HTTP2-high-perf-browser-networking.pdf
HTTP/2 新特性浅析:HTTP/2 新特性浅析
Kevin blog 关于 HTTP/2 的系列归档:HTTP/2 | 凯文叔叔的网志
Can i use 上关于支持HTTP/2 的浏览器:Can I use... Support tables for HTML5, CSS3, etc
http://http2-explained.haxx.se/content/en/part5.html
https://www.chromium.org/spdy/spdy-whitepape
本文主要内容来源:《HTTP 2.0的那些事》
文章由本人精炼而成,原文:再谈HTTP2性能提升之背后原理-HTTP2历史解剖 - Network - 周陆军的个人网站