3.4、HTTP连接的处理

3.4.1、Connection首部

由逗号分隔的连接标签列表指定了一些不会传播到其他连接中去的选项。

Connection 首部可以承载 3 种不同类型的标签:

  • HTTP 首部字段名,列出了只与此连接有关的首部。
  • 任意标签值,用于描述此连接的非标准选项。
  • 值close,说明操作完成之后需关闭这条持久连接。

如果连接标签中包含了一个 HTTP 首部字段的名称,那么这个首部字段就包含了 与一些连接有关的信息,不能将其转发出去,如下:

Connection

在将报文转发出去之前,必须删除 Connection 首部列出的所有首部字段。由于 Connection 首部可以防止无意中对 本地首部的转发,因此将逐跳首部名放入 Connection 首部被称为“对首部的保 护”;可能还会有少量没有作为 Connection 首部值列出,但是一定不能被代理转发的逐跳首部,其中包括 Prxoy-Authenticate、Proxy-Connection、Transfer-Encoding 和 Upgrade。

3.4.2、串行事务处理时延以及解决办法

串行加载引入的缺点:

  • 实际连接时延和慢启动时延。
  • 在加载了足够多的对象之前,无法在屏幕上显示任何内容,比如污染确定对象的尺寸和位置。

解决方法:并行连接、持久连接、管道化连接、复用的连接。

对比:

连接优化对比

并行连接

HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许客户端打开多条连接,并行地执行多个 HTTP 事务,比如并行加载四幅图片。

并行连接可能会提高页面的加载速度:克服了单条连接的空载时间和带宽限制;每个连接之间都会有一小段软件时延,不过连接请求和传输时间基本都是重叠起来的。

当客户端的网络带宽不足时,并行连接会导致有限带宽的竞争,每个连接以较慢速度按比例加载,并行连接不一定更快

打开大量连接会消耗很多内存资源,造成服务器或代理性能的严重下降,一般限制并行连接数量为4个

并行连接一般让页面有多个组件对象同时出现在屏幕上,给用户一种错觉,页面加载得很快,即使并行连接没有加快页面的传输速度。

持久连接

在事 务处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。持久连接可以避免缓慢的连接建立阶段以及慢启动的拥塞适应阶段。

相比并行连接,持久连接的好处:降低时延和连接建立的开销,后续无慢启动时延,减少打开连接的潜在数量。 缺点:可能累积大量的空闲连接。

持久连接与并行连接配合使用:打开少量的并行连接,每个并行连接为持久连接。

HTTP/1.0+ keep-alive连接

早期的实验性持久连接,已经不再使用,当前的 HTTP/1.1 规范无说明,但是对keep-alive握手的使用还是很广泛。

(1) 实现方法: 请求头包含Connection: Keep-Alive,若服务器支持则返回该首部,若不返回则说明服务器不支持而且关闭连接:

keep-alive

用 Keep-Alive 通用首部中指定的、由逗号分隔的选项来调节 keep-alive 的 行为,如下例子说明服务器最多还会为另外 5 个事务保持连接的打开状态,或者将打开状态保持到连接空闲了 2 分钟之后:

  1. Connection: Keep-Alive
  2. Keep-Alive: max=5 timeout=120

(2) 注意事项:

  • HTTP/1.0 中,客户端必须发送一个 Connection: Keep-Alive 请求首部来激活 keep-alive 连接。
  • Connection: Keep-Alive 首部必须随所有希望保持持久连接的报文一起发送。
  • 不应该与无法确定是否支持Connection首部的代理服务器建立 keep-alive 连接。
  • 从技术上来讲,应该忽略所有来自HTTP/1.0设备Connection首部字段,因为可能由老代理误转发的(老代理不知道要删除Connection首部再转发),但是有些客户端和服务器不忽略。

(3) Keep-Alive和哑代理

很多老的或简单的代理都是盲中继(blind relay),它们只是将字节从一个连接转发到另一个连接中去,不对 Connection 首部进行特殊的处理,将其作为一个扩展首部对待。

以下错误的通信方式会使浏览器一直处于挂起状态,直到客户端或服务器将连接 超时,并将其关闭为止:

哑代理

逐跳首部只与一条特定的连接有关,不能被转发。

为避免此类代理通信问题的发生,现代的代理都绝不能转发 Connection 首部 和所有名字出现在 Connection 值中的首部:Keep-Alive、Proxy-Authenticate、Proxy-Connection、 Transfer-Encoding 和 Upgrade。

(4) 插入Proxy-Connection

对盲中继问题的变通做法:引入了一个名为 Proxy-Connection 的新首部,解决了在客户端后面紧跟着一个盲中继所带来的问 题。对有多层次代理的情况,Proxy-Connection 仍然无法解决问题

实现方法:

浏览器会向代理发送非标准的 Proxy-Connection 扩展首 部,而不是官方支持的著名的 Connection 首部。如果代理是盲中继,它会将无意 义的 Proxy-Connection 首部转发给 W eb 服务器,服务器会忽略此首部,不会带 来任何问题。但如果代理是个聪明的代理(能够理解持久连接的握手动作),就用一 个 Connection 首部取代无意义的 Proxy-Connection 首部,然后将其发送给服 务器,以收到预期的效果。

如下图:

Proxy-Connection

HTTP/1.1 持久连接

与 HTTP/1.0+ 的 keep-alive 连接不同,HTTP/1.1 持久连接在默认情况下是激活 的。

要在事务处理结束 之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个 Connection: close 首部。

限制与规则:

  • 只有当连接上所有的报文都有正确的、自定义报文长度时——也就是说,实体主 体部分的长度都和相应的 Content-Length 一致,或者是用分块传输编码方式 编码的——连接才能持久保持。错误的 Content-Length 是很糟糕的事,因为事务处理的另一端无法精确地检测出一条报文的结束和另一条报文的开始了(keep-alive也是)。
  • HTTP/1.1 的代理必须能够分别管理与客户端和服务器的持久连接——每个持久 连接都只适用于一跳传输。
  • 一个用户客户端对任何服务器或代理最多只能维护两条持久连接,以防服务器过 载。代理可能需要更多到服务器的连接来支持并发用户的通信,所以,如果有 N 个用户试图访问服务器的话,代理最多要维持 2N 条到任意服务器或父代理的 连接。
管道化连接

HTTP/1.1 允许在持久连接上可选地使用请求管道。这是相对于 keep-alive 连接的又 一性能优化。

在响应到达之前,可以将多条请求放入队列,当第一条的请求通过网络流向服务器时,第二条和第三条请求也可以开始发送了,从而降低网络的环回时间,提高性能。

限制:

  • 如果HTTP客户端无法确认连接是持久的,就不应该使用管道。
  • 必须按照与请求相同的顺序回送HTTP响应,因为无序列号标签。
  • HTTP 客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完 成的管道化请求。
  • HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(比如 POST)。