zoukankan      html  css  js  c++  java
  • 再谈HTTP2性能提升之背后原理—HTTP2历史解剖

    即使千辛万苦,还是把网站升级到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住,要处理好业务数据的过期机制。

    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的发展历史

    一、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的关系比较复杂。

    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 - 周陆军的个人网站

  • 相关阅读:
    arcgis api 3.x for js 入门开发系列八聚合效果(附源码下载)
    arcgis api 3.x for js 入门开发系列七图层控制(附源码下载)
    arcgis api 3.x for js 入门开发系列六地图分屏对比(附源码下载)
    arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
    arcgis api 3.x for js 入门开发系列四地图查询(附源码下载)
    Java里面获取当前服务器的IP地址
    Flutter at Google I/O 2018
    Modbus RTU 协议使用汇总
    plsql 创建表空间、用户、赋予权限
    Oracle:ODP.NET Managed 小试牛刀
  • 原文地址:https://www.cnblogs.com/zhoulujun/p/10329279.html
Copyright © 2011-2022 走看看