HTTP连接管理(2)

持久并行连接

  本节对持久连接的两种方式:HTTP/1.0+ 中的 "keep-alive" 和 HTTP/1.1 中的 "persistent" 连接进行详细的介绍。

HTTP/1.0+ keep-alive 连接

  keep-alive 是一个早期的实验性方案,在 1996 年很多 HTTP/1.0 浏览器和服务器都进行了扩展以支持这种实验型持久连接。这些早期的持久连接受到了一些互操作性设计方面问题的困扰——尽管这些问题在后期的 HTTP/1.1 版本中都得到了修正,但仍然有很多客户端和服务器使用较早的 keep-alive 连接。

  keep-alive 已经不再使用,现行的 HTTP/1.1 规范中也没有了对它的说明,但浏览器和服务器对 keep-alive 握手的使用仍然相当广泛,所以我们来简单浏览一下 keep-alive 的操作。

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

Keep-Alive 选项

  Keep-Alive 首部只是 请求 将连接保持在活跃状态,发出 Keep-Alive 请求后,客户端和服务器 * 并不一定会 同意进行 Keep-Alive 会话,它们可以在任意时刻关闭空闲的 Keep-Alive 连接,并可随意限制 keep-alive 连接所处理事物数量。

  可以用 Keep-Alive 通用首部中指定的,由逗号分隔的选项来调节 keep-alive 的行为。

Keep-Alive 连接的限制和规则

  • 在 HTTP/1.0 中,keep-alive 并不是默认使用的,客户端必须发送一个Connection:Keep-Alive请求首部以激活 keep-alive 连接。
  • Connetion:Keep-Alive 首部必须随所有希望保持持久连接的报文一起发送。如果客户端没有发送 Connection:Keep-Alive 首部,服务器就会在那条请求之后关闭连接。
  • 客户端探明响应中没有 Connection:Keep-Alive 响应首部,就可以知道服务器发出响应后是否会关闭连接了。
  • 只有在无需检测到连接的关闭即可确定报文实体主体部分长度的情况下,才能将连接保持在打开状态——也就是说实体的主体部分必须有正确的 Content-Length。
  • 代理和网关必须执行 Connection 首部的规则,在将报文转发出去或将其高速缓存前,删除在 Connection 首部中命名的所有首部字段以及 Connection 首部自身。
  • 不应该与 无法确定是否支持 Connection 首部的代理服务器建立 Keep-Alive 连接 ,以防止出现哑代理问题。
  • 从技术上来讲,应该忽略所有来自 HTTP/1.0 设备的 Connection 首部字段(包括 Connection:Keep-Alive),因为它们可能是由比较老的代理服务器误转发的。
  • 除非重复发送请求会产生一些其他的副作用,否则如果在客户端收到完整的响应前连接就关闭了,客户端就一定要做好重试请求的准备。

哑代理

Connection 首部和盲中继

  这个问题出现在代理上——不理解 Connection 首部而且也不知道在沿着转发链路将其发送之前应该把首部删除的代理。很多老的或简单的代理都是 盲中继 。它们只是将字节从一个连接转发到另一个连接上去,不对 Connection 首部进行特殊的处理。

代理和逐跳首部

  为避免此类代理通信问题的发生,现代的代理都绝对不能转发 Connection 首部和所有名字出现在 Connection 值中的首部。

插入 Proxy-Conneciton

  代理实现者们提出了一个对盲中继问题的变通做法,这种做法并不要求所有的 Web 应用程序支持高版本的 HTTP——它引入了一个名为 Proxy-Connection 的新首部,解决了在客户端后面紧跟着一个盲中继所带来的问题——但并没有解决所有其他情况下存在的问题。在显示配置了代理的情况下,现代浏览器都实现了 Proxy-Connection,很多代理都能理解它。

  问题是哑代理盲目地转发 Connection:Keep-Alive 之类的逐跳首部,逐跳首部只与一条特定的连接有关,不能被转发 ,当下游服务器误将转发来的首部作为来自代理自身的请求支持,就会出现预料之外的问题、

  变通的做法是,浏览器会向代理发送非标准的 Proxy-Connection 扩展首部,而不是官方支持的 Connection 首部。如果代理是盲中继,它会将无意义的 Proxy-Connection 首部转发给 web 服务器

,服务器会忽略此首部。

HTTP/1.1 持久连接

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

  与 HTTP/1.0+ 的 keep-alive 不同,HTTP/1.1 持久连接在 默认情况下是激活的 。除非特别指明,否则 HTTP/1.1 假定所有连接都是持久的 。要在事物处理结束之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个 Connection:Close 首部。

  但是,客户端和服务器仍然可以随时关闭空闲连接,不发生 Connection:Close 并不意味着服务器承诺永久得保持打开状态。

持久连接的限制和规则

  • 发送了 Connection:Close 请求首部之后,客户端就无法在这条连接上发送更多的请求
  • 如果客户端不想在连接上发送其他请求了,就应该在最后一条请求中发送一个 Connection:Close 请求首部
  • 只有当连接上的所有报文都有正确的、自定义报文长度(实体主体部分的长度都和相应的 Connection-Length 一致,或者是分块传输编码方式编码的)时,连接才能持久保持。
  • HTTP/1.1 的代理必须能够分别管理与客户端和服务器的持久连接,每个持久连接都只适用于一跳传输
  • HTTP/1.1 设备可以在任意时刻关闭连接,而无论 Connection 首部取了什么值。
  • HTTP/1.1 应用程序必须能够从异步的关闭中恢复出来
  • 除非重复发起请求会产生副作用,否则如果在客户端收到整条响应之前连接被关闭了,客户端就必须重新发起请求

管道化连接

  HTTP/1.1 运行在持久连接基础上使用 请求管道 ,是在 keep-alive 连接上的进一步性能优化。在响应到达之前,可以将多条请求放入队列,当第一条请求通过网络流向地球另一端时的服务器时,第二条和第三天请求也可以开始发送了,这样做可以降低网络的环回时间,提高性能。

对管道化连接有几点限制:

  • 如果 HTTP 客户端无法确认连接是永久的,就不应该使用管道
  • 必须按照与请求相同的顺序回送 HTTP 响应,如过收到的响应失序,就没有办法将其与请求匹配起来。
  • HTTP 客户端必须做好连接会在任意时刻被关闭的准备,还要准备好重发所有未完成的管道化请求。
  • HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(如 POST)