即便千辛万苦,仍是把网站升级到http2了,遇坑如《phpcms v9站http升级到https加http2遇到到坑》。javascript
由于理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 彻底语义,进一步减小了网络延迟。php
对于前端开发人员来讲,无疑减小了在前端方面的优化工做。好比雪碧图&文件合并||内容内嵌||域名分片html
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协议头里能够设置Connection:Keep-Alive。在header里设置Keep-Alive能够在必定时间内复用链接,具体复用时间的长短能够由服务器控制,通常在15s左右。到http1.1以后Connection的默认值就是Keep-Alive,若是要关闭链接复用须要显式的设置Connection:Close。一段时间内的链接复用对PC端浏览器的体验帮助很大,由于大部分的请求在集中在一小段时间之内。但对移动app来讲,成效不大,app端的请求比较分散且时间跨度相对较大。因此移动端app通常会从应用层寻求其它解决方案,长链接方案或者伪长链接方案:git
如今愈来愈多的移动端app都会创建一条本身的长连接通道,通道的实现是基于tcp协议。基于tcp的socket编程技术难度相对复杂不少,并且须要本身制定协议,但带来的回报也很大。信息的上报和推送变得更及时,在请求量爆发的时间点还能减轻服务器压力(http短链接模式会频繁的建立和销毁链接)。不止是IM app有这样的通道,像淘宝这类电商类app都有本身的专属长链接通道了。如今业界也有很多成熟的方案可供选择了,google的protobuf就是其中之一。github
客户端在初始状态就会发送一个polling请求到服务器,服务器并不会立刻返回业务数据,而是等待有新的业务数据产生的时候再返回。因此链接会一直被保持,一旦结束立刻又会发起一个新的polling请求,如此反复,因此一直会有一个链接被保持。服务器有新的内容产生的时候,并不须要等待客户端创建一个新的链接。作法虽然简单,但有些难题须要攻克才能实现稳定可靠的业务框架:web
和传统的http短连接相比,长链接会在用户增加的时候极大的增长服务器压力算法
移动端网络环境复杂,像wifi和4g的网络切换,进电梯致使网络临时断掉等,这些场景都须要考虑怎么重建健康的链接通道。
这种polling的方式稳定性并很差,须要作好数据可靠性的保证,好比重发和ack机制。
polling的response有可能会被中间代理cache住,要处理好业务数据的过时机制。
long-polling方式还有一些缺点是没法克服的,好比每次新的请求都会带上重复的header信息,还有数据通道是单向的,主动权掌握在server这边,客户端有新的业务请求的时候没法及时传送。
同long-polling不一样的是,server并不会结束初始的streaming请求,而是持续的经过这个通道返回最新的业务数据。显然这个数据通道也是单向的。streaming是经过在server response的头部里增长”Transfer Encoding: chunked”来告诉客户端后续还会有新的数据到来。除了和long-polling相同的难点以外,streaming还有几个缺陷:
有些代理服务器会等待服务器的response结束以后才会将结果推送到请求客户端。对于streaming这种永远不会结束的方式来讲,客户端就会一直处于等待response的过程当中。
业务数据没法按照请求来作分割,因此客户端没收到一块数据都须要本身作协议解析,也就是说要作本身的协议定制。
streaming不会产生重复的header数据。
WebSocket和传统的tcp socket链接类似,也是基于tcp协议,提供双向的数据通道。WebSocket优点在于提供了message的概念,比基于字节流的tcp socket使用更简单,同时又提供了传统的http所缺乏的长链接功能。不过WebSocket相对较新,2010年才起草,并非全部的浏览器都提供了支持。各大浏览器厂商最新的版本都提供了支持。
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的问题描述。
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 链接的减小而使网络拥塞情况得以改善,同时慢启动时间的减小,使拥塞和丢包恢复速度更快
众所周知 ,在 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)的特性,因此链接的重用老是比新建链接性能要好。
下图总结了不一样浏览器对该限制的数目。
这也是为什么一些站点会有多个静态资源 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浏览商品的时候,快速的滑动到了商品列表的底部,但前面的请求先发出,若是不把后面的请求优先级设高,用户当前浏览的图片要到最后才能下载完成,显然体验没有设置优先级好。同理依赖在有些场景下也有妙用。
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,表现更好。
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的部署出了问题。
HTTP2.0使用了tls的拓展ALPN来作协议升级,除此以外加密这块还有一个改动,HTTP2.0对tls的安全性作了近一步增强,经过黑名单机制禁用了几百种再也不安全的加密算法,一些加密算法可能还在被继续使用。若是在ssl协商过程中,客户端和server的cipher suite没有交集,直接就会致使协商失败,从而请求失败。在server端部署http2.0的时候要特别注意这一点。
典型问题:
详情参考另外一个答案:
HTTP/2 对如今的网页访问,有什么大的优化呢?体如今什么地方
PS:
强烈推荐阅读 Mark Nottingham 在 Velocity Beijing 2015 的 speech:HTTP/2 for Front-End Developers ,关于 HTTP/2 下的前端性能优化相关。
Slide 地址:HTTP/2 for Front-End Developers
HTTP协议通过多年的使用,发现了一些不足,主要是性能方面的,包括:
HTTP的链接问题,HTTP客户端和服务器之间的交互是采用请求/应答模式,在客户端请求时,会创建一个HTTP链接,而后发送请求消息,服务端给出应答消息,而后链接就关闭了。(后来的HTTP1.1支持持久链接)
由于TCP链接的创建过程是有开销的,若是使用了SSL/TLS开销就更大。
在浏览器里,一个网页包含许多资源,包括HTML,CSS,JavaScript,图片等等,这样在加载一个网页时要同时打开链接到同一服务器的多个链接。
HTTP消息头问题,如今的客户端会发送大量的HTTP消息头,因为一个网页可能须要50-100个请求,就会有至关大的消息头的数据量。
HTTP通讯方式问题,HTTP的请求/应答方式的会话都是客户端发起的,缺少服务器通知客户端的机制,在须要通知的场景,如聊天室,游戏,客户端应用须要不断地轮询服务器。
SPDY的主要目的是减小50%以上的页面加载时间,可是呢不增长部署的复杂性,不影响客户端和服务端的Web应用,只须要浏览器和Web服务器支持SPDY。主要有如下几:
多路复用,一个TCP链接上同时跑多个HTTP请求。请求可设定优先级。
去除不须要的HTTP头,压缩HTTP头,以减小须要的网络带宽。
使用了SSL做为传输协议提供数据安全。
对传输的数据使用gzip进行压缩
提供服务方发起通讯,并向客户端推送数据的机制。
SPDY的解决办法就是设计了一个会话层协议--帧协议,解决多路复用,优先级等问题,而后在其上实现了HTTP的语义。
SPDY的诞生和表现说明了两件事情:一是在现有互联网设施基础和http协议普遍使用的前提下,是能够经过修改协议层来优化http1.x的。二是针对http1.x的修改确实效果明显并且业界反馈很好。正是这两点让IETF(Internet Enginerring Task Force)开始正式考虑制定HTTP2.0的计划,最后决定以SPDY/3为蓝图起草HTTP2.0,SPDY的部分设计人员也被邀请参与了HTTP2.0的设计。
WebSocket则提供使用一个TCP链接进行双向通信的机制,包括网络协议和API,以取代网页和服务器采用HTTP轮询进行双向通信的机制。
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中所作的。
还有一个有趣的技术叫作HTTP Speed+Mobility,和SPDY同样都是HTTP 2.0标准的竞争者,HTTP Speed+Mobility来自微软。HTTP SM借鉴了SPDY和WebSocket的协议,将两者揉为一体,又有所取舍。
保留HTTP的语义,这一点和SPDY一致,但也正应如此,抛弃了SPDY里的ServerPush。
遵照分层的网络架构,TCP能作的,HTTP SM不作,所以去除了SPDY的流控。
使用现有标准,所以使用HTTP/1.1 Upgrade header机制,借用了WebSocket的握手机制和帧格式(RFC6455)。
客户端掌握内容的控制,所以不强制使用压缩和SSL/TLS。
考虑到网络的费用和电力,这点考虑到了移动设备以及物联网,提供了Credit Control机制。
会话层和帧协议,这部分取自WebSocket协议。包括握手机制,以及帧格式。
流层(包括多路复用),这部分主要借鉴SPDY,包括多路复用,流优先级,但增长了Credit Control。这部分做为 WebSocket协议的扩展。
HTTP层,在流层上实现HTTP语义,这部分也借鉴自SPDY。
NF是HTTP 2.0候选方案之一,主要提出如下改进:
对HTTP头的名称进行二进制编码
对通用HTTP头进行分组
请求/应答的多路复用
分层模型
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个小建议:
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
https://www.chromium.org/spdy/spdy-whitepape
本文主要内容来源:《HTTP 2.0的那些事》
文章由本人精炼而成,原文:再谈HTTP2性能提高之背后原理-HTTP2历史解剖 - Network - 周陆军的我的网站