HTTP连接管理之并行连接、持久化连接(Keep-Alive、Proxy-Connection)、管道化连接

串行事务处理时延

在介绍连接方式之前,先介绍一下串行事务处理时延

  • 如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。比如,假设有一个包含了3个嵌入图片的Web页面。浏览器需要发起 4 个 HTTP 事务来显示此页面: 1 个用于顶层的 HTML页面,3 个用于嵌入的图片。如果每个事务都需要(串行地建立)一条新的连接,那么连接时延和慢启动时延就会叠加起来

watermarktype_ZmFuZ3poZW5naGVpdGkshadow_10text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDUzMjg1size_16color_FFFFFFt_70

  • 串行加载的另一个缺点:有些浏览器在对象加载完毕之前无法获知对象的尺寸, 而且它们可能需要尺寸信息来决定将对象放在屏幕的什么位置上,所以在加载了足 够多的对象之前,无法在屏幕上显示任何内容。在这种情况下,可能浏览器串行装 载对象的进度很正常,但用户面对的却是一个空白的屏幕,对装载的进度一无所知

一些提高HTTP连接性能的技术:

  • ①并行连接:通过多条 TCP 连接发起并发的 HTTP 请求
  • ②持久连接:重用 TCP 连接,以消除连接及关闭时延
  • ③管道化连接:通过共享的 TCP 连接发起并发的 HTTP 请求
  • ④复用的连接:交替传送请求和响应报文(实验阶段)

一、并行接连

1.1并行连接的原理

  • HTTP 允许客户端打开多条连接,并行地执行多个HTTP事务。在这个例子中,并行加载了四幅嵌入式图片,每个事务都有自己的 TCP 连接

watermarktype_ZmFuZ3poZW5naGVpdGkshadow_20text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDUzMjg2size_26color_FFFFFFt_70

1.2并行连接可能会提高页面的加载速度

  • 包含嵌入对象的组合页面如果能(通过并行连接)克服单条连接的空载时间和带宽限制,加载速度也会有所提高。时延可以重叠起来,而且如果单条连接没有充分利用客户端的因特网带宽,可以将未用带宽分配来装载其他对象
  • 下图显示了并行连接的时间线,比串行传输要快得多。首先装载的是封闭的 HTML页面,然后并行处理其余的3个事务,每个事务都有自己的连接。图片的装载是并行的,连接的时延也是重叠的

watermarktype_ZmFuZ3poZW5naGVpdGkshadow_10text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDUzMjg1size_16color_FFFFFFt_7

1.3并行连接不一定更快

  • 情景一:客户端的网络带宽不足 (比如,浏览器是通过一个 28.8kbps 的 Modem 连接到因特网上去的)时,大部分 的时间可能都是用来传送数据的。在这种情况下,一个连接到速度较快服务器上的 HTTP 事务就会很容易地耗尽所有可用的 Modem 带宽。如果并行加载多个对象,每 个对象都会去竞争这有限的带宽,每个对象都会以较慢的速度按比例加载,这样带 来的性能提升就很小,甚至没什么提升
  • 情景二:打开大量连接会消耗很多内存资源,从而引发自身的性能问题。复杂的Web页面可能会有数十或数百个内嵌对象。客户端可能可以打开数百个连接,但Web服务器通常要同时处理很多其他用户的请求,所以很少有Web服务器希望出现这样的 情况。一百个用户同时发出申请,每个用户打开100个连接,服务器就要负责处理 10 000 个连接。这会造成服务器性能的严重下降。对高负荷的代理来说也同样如此

实际上,浏览器确实使用了并行连接,但它们会将并行连接的总数限制为一个较小的值(通常是 4 个)。服务器可以随意关闭来自特定客户端的超量连接

二、持久化连接

  • 概念:HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的 HTTP 请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。非持久连接会在每个事务结束之后关闭
  • 缺点:管理持久连接时要特别小心,不然就会累积出大量的空闲连接,耗费本地以及远程客户端和服务器上的资源

持久化连接的类型:

  • HTTP/1.0+的“keep-alive”连接
  • HTTP/1.1的“persistent”连接

持久化连接与并行连接的关系:

并行连接的一些缺点:

  • 每个事务都会打开/关闭一条新的连接,会耗费时间和带宽
  • 由于TCP慢启动特性的存在,每条新连接的性能都会有所降低
  • 可打开的并行连接数量实际上是有限的

持久化优点:持久连接降低了时延和连接建立的开销, 将连接保持在已调谐状态,而且减少了打开连接的潜在数量

持久化连接与并行连接配合使用:持久连接与并行连接配合使用可能是最高效的方式。现在,很多Web应用程序都会打开少量的并行连接,其中的每一个都是持久连接

持久化连接对串行连接的改进:

  • 下图将在串行连接上实现4个 HTTP 事务的时间线与在一条持久连接上实现同样事务所需的时间线进行了比较。 由于去除了进行连接和关闭连接的开销,所以时间线有所缩减

watermarktype_ZmFuZ3poZW5naGVpdGkshadow_30text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDUzMjg3size_36color_FFFFFFt_70

2.1HTTP1.0版本的keep-alive连接

①概念

  • keep-alive已经不再使用了,而且在当前的HTTP/1.1规范中也没有对它的说明了。 但浏览器和服务器对keep-alive握手的使用仍然相当广泛

工作原理:

  • 客户端可以通过包含Connection: Keep-Alive 首部请求将一条连接保持在打开状态
  • 如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有Connection: Keep-Alive 首部,客户端就认为服务器不支持keep-alive,会在发回响应报文之后关闭连接

watermarktype_ZmFuZ4poZW5naGVpdGkshadow_40text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L4FxXzQxNDUzMjg4size_46color_FFFFFFt_70

Keep-Alive首部:

  • Keep-Alive首部完全是可选的,但只有在提供Connection: Keep-Alive 时才能使用它(激活)
  • keep-Alive首部只是请求将连接保持在活跃状态。发出keep-alive请求之 后,客户端和服务器并不一定会同意进行 keep-alive 会话。它们可以在任意时刻关闭空闲的 keep-alive 连接,并可随意限制 keep-alive 连接所处理事务的数量

首部参数:

  • 参数timeout:它估计了服务器希望将连接保持在活跃状态的时间。这并不是一个承诺值
  • 参数max:它估计了服务器还希望为多少个事务保持此连接的活跃状态。这并不是一个承诺值
  • 其他参数属性:Keep-Alive首部还可支持任意未经处理的属性,这些属性主要用于诊断和调试

案例:

  • 服务器最多还会为另外5个事务保持连接的打开状态,或者将打开状态保持到连接空闲了2分钟之后

20190709173522180

Keep-Alive连接的限制和规则:

  • keep-alive 并不是默认使用的。 客户端必须发送一个Connection: Keep-Alive请求首部来激活keep-alive连接
  • Connection: Keep-Alive 首部必须随所有希望保持持久连接的报文一起发送。 如果客户端没有发送Connection: Keep-Alive 首部,服务器就会在那条请求之后关闭连接
  • 通过检测响应中是否包含Connection: Keep-Alive响应首部,客户端可以判断服务器是否会在发出响应之后关闭连接
  • 只有在无需检测到连接的关闭即可确定报文实体主体部分长度的情况下,才能将连接保持在打开状态——也就是说实体的主体部分必须有正确的 ContentLength,有多部件媒体类型,或者用分块传输编码的方式进行了编码。在一条 keep-alive 信道中回送错误的 Content-Length 是很糟糕的事,这样的话,事务处理的另一端就无法精确地检测出一条报文的结束和另一条报文的开始了
  • 代理和网关必须执行Connection首部的规则。代理或网关必须在将报文转发出去或将其高速缓存之前,删除在Connection首部中命名的所有首部字段以及Connection首部自身
  • 严格来说,不应该与无法确定是否支持Connection首部的代理服务器建立keep-alive连接,以防止出现哑代理问题。在实际应用中不是总能做到这一点的
  • 从技术上来讲,应该忽略所有来自HTTP/1.0设备的Connection首部字段(包括 Connection: Keep-Alive),因为它们可能是由比较老的代理服务器误转发 的。但实际上,尽管可能会有在老代理上挂起的危险,有些客户端和服务器还是会违反这条规则
  • 除非重复发送请求会产生其他一些副作用,否则如果在客户端收到完整的响应之前连接就关闭了,客户端就一定要做好重试请求的准备

哑代理

  • 哑代理的情景出现在:那些不理解Connection首部(代理不理解/不支持Connection首部),而且不知道在沿着转发链路将其发送出去之前,应该将该首部删除的代理。很多老的或简单的代理都是盲中继,它们只是将字节从一个连接转发到另一个连接中去,不对Connection首部进行特殊的处理

演示案例:

watermarktype_ZmFuZ5poZW5naGVpdGkshadow_50text_aHR0cHM6Ly9ibG9nLmNzZG5ubmV0L5FxXzQxNDUzMjg5size_56color_FFFFFFt_70

  • 第一步:Web客户端向代理发送了一条报文,其中包含了 Connection: Keep-Alive 首部,如果可能的话请求建立一条 keep-alive 连接。客户端等待响应,以确定对方是否认可它对keep-alive信道的请求
  • 第二步:哑代理收到了这条HTTP请求,但它并不理解Connection首部(只是将其作为一个扩展首部对待)。代理不知 keep-alive 是什么意思,因此只是沿着转发链路将报文一字不漏地发送给服务器。但Connection首部是个逐跳首部,只适用于单条传输链路,不应该沿着传输链路向下传输
  • 第三步:经过中继的HTTP请求抵达了Web服务器。当 Web服务器收到经过代理转发的 Connection: Keep-Alive首部时,会误以为代理(对服务器来说,这个代理看起来就和所有其他客户端一样)希望进行keep-alive对话!然后Web服务器同意进行keep-alive对话,并在图中c回送了一个Connection: Keep-Alive响应首部。此时,Web 服务器认为它在与代理进行keep-alive对话,会遵循keep-alive的规则。但代理却对keepalive一无所知
  • 第四步:哑代理将Web服务器的响应报文回送给客户端,并将来自Web服务器的 Connection: Keep-Alive 首部一起传送过去。客户端看到这个首部,就会认为代理同意进行keep-alive对话。所以,此时客户端和服务器都认为它们在进行keep-alive对话,但与它们进行对话的代理却对keep-alive一无所知
  • 第五步:由于代理对keep-alive一无所知,所以会将收到的所有数据都回送给客户端,然后等待源端服务器关闭连接。但源端服务器会认为代理已经显式地请求它将连接保持在打开状态了,所以不会去关闭连接。这样,代理就会挂在那里等待连接的关闭
  • 第六步:客户端在图中d收到了回送的响应报文时,会立即转向下一条请求,在keepalive连接上向代理发送另一条请求(图中的e)。而代理并不认为同一条连接上会有其他请求到来,请求被忽略,浏览器就在这里转圈,不会有任何进展了
  • 第七步:这种错误的通信方式会使浏览器一直处于挂起状态,直到客户端或服务器将连接超时,并将其关闭为止

代理和逐跳首部的关系:

  • 为避免此类代理通信问题的发生,现代的代理都绝不能转发 Connection 首部 和所有名字出现在 Connection 值中的首部。因此,如果一个代理收到了一个 Connection: Keep-Alive 首部,是不应该转发 Connection 首部,或所有名为 Keep-Alive 的首部的。另外,还有几个不能作为 Connection 首部值列出,也不能被代理转发或作为缓存响应使用的首部。其中包括 Proxy-Authenticate、Proxy-Connection、 Transfer-Encoding 和 Upgrade

Proxy-Connection首部

  • 设计原理:哑代理盲目地转发 Connection: Keep-Alive 之类的逐跳首部惹出了麻烦。所以引入了一个名为Proxy-Connection的新首部,解决了在客户端后面紧跟着一个盲中继所带来的问 题——但并没有解决所有其他情况下存在的问题
  • 工作原理:如果代理是盲中继,它会将无意义的 Proxy-Connection 首部转发给 Web 服务器,服务器会忽略此首部,不会带 来任何问题。但如果代理是个聪明的代理(能够理解持久连接的握手动作),就用一 个 Connection 首部取代无意义的 Proxy-Connection 首部,然后将其发送给服 务器,以收到预期的效果

案例一:

  • 显示了盲中继是如何向Web 服务器转发Proxy-Connection 首部,而不带来任何问题的,Web 服务器忽略了这个首部,这样在客户端和代理, 或者代理和服务器之间就不会建立起 keep-alive 连接了

watermarktype_ZmFuZ6poZW6naGVpdGkshadow_60text_aHR0cHM6Ly9ibG9nLmNzZG6ubmV0L6FxXzQxNDUzMjg6size_66color_FFFFFFt_70

案例二:

  • 下图那个聪明的代理知道Proxy-Connection 首部是对 keep-alive 对话的请求,它会发送自 己的 Connection: Keep-Alive 首部来建立 keep-alive 连接

watermarktype_ZmFuZ7poZW7naGVpdGkshadow_70text_aHR0cHM7Ly9ibG9nLmNzZG7ubmV0L7FxXzQxNDUzMjg7size_77color_FFFFFFt_70

Proxy-Connection首部的缺陷:

  • ①在客户端和服务器之间只有一个代理时可以用这种方案来解决问题。但是如果在哑代理的任意一侧还有一个聪明的代理(如下图),这个问题就会再次露头了

watermarktype_ZmFuZ8poZW8naGVpdGkshadow_80text_aHR0cHM8Ly9ibG9nLmNzZG8ubmV0L8FxXzQxNDUzMjg8size_88color_FFFFFFt_80

  • ②而且,网络中出现“不可见”代理的情况现在变得很常见了,这些代理可以是防火 墙、拦截缓存,或者是反向代理服务器的加速器。这些设备对浏览器是不可见的, 所以浏览器不会向它们发送 Proxy-Connection 首部。透明的Web应用程序正确 地实现持久连接是非常重要的

2.2HTTP/1.1的持久连接

  • HTTP/1.1逐渐停止了对 keep-alive 连接的支持,用一种名为持久连接(persistent connection)的改进型设计取代了它。持久连接的目的与 keep-alive 连接的目的相同,但工作机制更优一些

特点:

  • ①与 HTTP/1.0+的keep-alive连接不同,HTTP/1.1 持久连接在默认情况下是激活的。除非特别指明,否则HTTP/1.1假定所有连接都是持久的
  • ②要在事务处理结束之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个Connection: close首部
  • ③HTTP/1.1客户端假定在收到响应后,除非响应中包含了Connection: close首 部,不然HTTP/1.1连接就仍维持在打开状态。但是,客户端和服务器仍然可以随时关闭空闲的连接。不发送Connection: close并不意味着服务器承诺永远将连接保持在打开状态

持久连接的限制和规则:

  • 发送了Connection: close 请求首部之后,客户端就无法在那条连接上发送更多的请求了
  • 如果客户端不想在连接上发送其他请求了,就应该在最后一条请求中发送一个 Connection: close 请求首部
  • 只有当连接上所有的报文都有正确的、自定义报文长度时——也就是说,实体主体部分的长度都和相应的 Content-Length 一致,或者是用分块传输编码方式编码的——连接才能持久保持
  • HTTP/1.1的代理必须能够分别管理与客户端和服务器的持久连接——每个持久连接都只适用于一跳传输
  • (由于较老的代理会转发Connection 首部,所以)HTTP/1.1的代理服务器不应该与 HTTP/1.0客户端建立持久连接,除非它们了解客户端的处理能力。实际上, 这一点是很难做到的,很多厂商都违背了这一原则
  • 尽管服务器不应该试图在传输报文的过程中关闭连接,而且在关闭连接之前至少应该响应一条请求,但不管 Connection 首部取了什么值,HTTP/1.1 设备都可以在任意时刻关闭连接
  • HTTP/1.1 应用程序必须能够从异步的关闭中恢复出来。只要不存在可能会累积起来的副作用,客户端都应该重试这条请求
  • 除非重复发起请求会产生副作用,否则如果在客户端收到整条响应之前连接关闭了,客户端就必须要重新发起请求
  • 一个用户客户端对任何服务器或代理最多只能维护两条持久连接,以防服务器过载。代理可能需要更多到服务器的连接来支持并发用户的通信,所以,如果有N个用户试图访问服务器的话,代理最多要维持2N条到任意服务器或父代理的连接

三、管道化连接

  • 概念及原理:HTTP/1.1允许在持久连接上可选地使用请求管道。这是相对于keep-alive连接的又 一性能优化。在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网 络条件下,这样做可以降低网络的环回时间,提高性能

管道化连接有几条限制:

  • 如果 HTTP 客户端无法确认连接是持久的,就不应该使用管道
  • 必须按照与请求相同的顺序回送HTTP响应。HTTP报文中没有序列号标签,因此如果收到的响应失序了,就没办法将其与请求匹配起来了
  • HTTP客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完成的管道化请求。如果客户端打开了一条持久连接,并立即发出了10条请求, 服务器可能在只处理了,比方说,5条请求之后关闭连接。剩下的5条请求会失败, 客户端必须能够应对这些过早关闭连接的情况,重新发出这些请求
  • HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(比如POST)。总 之,出错的时候,管道化方式会阻碍客户端了解服务器执行的是一系列管道化请求中的哪一些。由于无法安全地重试POST这样的非幂等请求,所以出错时,就存在某些方法永远不会被执行的风险

图示:

  • 下图显示了持久连接是怎样消除 TCP 连接时延,以及管道化请求是如何消除传输时延的

watermarktype_ZmFuZ9poZW9naGVpdGkshadow_90text_aHR0cHM9Ly9ibG9nLmNzZG9ubmV0L9FxXzQxNDUzMjg9size_99color_FFFFFFt_90

 

原文:https://blog.csdn.net/qq_41453285/article/details/95163070