3月阅读周·HTTP权威指南:连接管理之持久连接篇
引言
HTTP(Hypertext Transfer Protocol,超文本传输协议[插图])是在万维网上进行通信时所使用的协议方案。HTTP有很多应用,但最著名的是用于Web浏览器和Web服务器之间的双工通信。
《HTTP权威指南》一书将HTTP中一些互相关联且常被误解的规则梳理清楚,并编写了一系列基于各种主题的章节介绍HTTP各方面的特性。纵观全书,对HTTP“为什么”这样做进行了详细的解释,而不仅仅停留在它是“怎么做”的。此外,这本书还介绍了很多HTTP应用程序正常工作所必需且重要的非HTTP技术。
这本书主要包括以下内容:
- 第一部分描述了Web的基础构件与HTTP的核心技术
- 第二部分重点介绍了Web系统的结构构造块:HTTP服务器、代理、缓存、网关以及机器人应用程序。
- 第三部分提供了一套用于追踪身份、增强安全性以及控制内容访问的技术和技巧。
- 第四部分涵盖HTTP报文主体和Web标准,前者包含实际内容,后者描述并处理主体内容。
- 第五部分介绍了发布和传播Web内容的技巧。
- 第六部分是一些很有用的参考附录,以及相关技术的教程。
连接管理
HTTP规范对HTTP报文解释得很清楚,但对HTTP连接介绍的并不多,HTTP连接是HTTP报文传输的关键通道。编写HTTP应用程序的程序员需要理解HTTP连接的来龙去脉以及如何使用这些连接。
持久连接
Web客户端经常会打开到同一个站点的连接。比如,一个Web页面上的大部分内嵌图片通常都来自同一个Web站点,而且相当一部分指向其他对象的超链通常都指向同一个站点。因此,初始化了对某服务器HTTP请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,获取在线图片)。这种性质被称为站点局部性(site locality)。
因此,HTTP/1.1(以及HTTP/1.0的各种增强版本)允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。
持久以及并行连接
我们看到,并行连接可以提高复合页面的传输速度。但并行连接也有一些缺点。
- 每个事务都会打开/关闭一条新的连接,会耗费时间和带宽。
- 由于TCP慢启动特性的存在,每条新连接的性能都会有所降低。
- 可打开的并行连接数量实际上是有限的。
持久连接有一些比并行连接更好的地方。持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量。但是,管理持久连接时要特别小心,不然就会累积出大量的空闲连接,耗费本地以及远程客户端和服务器上的资源。
持久连接与并行连接配合使用可能是最高效的方式。现在,很多Web应用程序都会打开少量的并行连接,其中的每一个都是持久连接。持久连接有两种类型:比较老的HTTP/1.0+“keep-alive”连接,以及现代的HTTP/1.1“persistent”连接。在接下来的几节中我们将对这两种类型进行介绍。
HTTP/1.0+ keep-alive连接
大约从1996年开始,很多HTTP/1.0浏览器和服务器都进行了扩展,以支持一种被称为keep-alive连接的早期实验型持久连接。这些早期的持久连接受到了一些互操作性设计方面问题的困扰,这些问题在后期的HTTP/1.1版本中都得到了修正,但很多客户端和服务器仍然在使用这些早期的keep-alive连接。
在串行连接上实现4个HTTP事务的时间线与在一条持久连接上实现同样事务所需的时间线进行了比较。由于去除了进行连接和关闭连接的开销,所以时间线有所缩减。
Keep-Alive操作
keep-alive已经不再使用了,而且在当前的HTTP/1.1规范中也没有对它的说明了。但浏览器和服务器对keep-alive握手的使用仍然相当广泛,因此,HTTP的实现者应该做好与之进行交互操作的准备。现在我们来快速浏览一下keep-alive的操作。对keep-alive握手更详细的解释请参见较早的HTTP/1.1规范版本(比如RFC 2068)。
实现HTTP/1.0 keep-alive连接的客户端可以通过包含Connection: Keep-Alive首部请求将一条连接保持在打开状态。
如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有Connection: Keep-Alive首部,客户端就认为服务器不支持keep-alive,会在发回响应报文之后关闭连接。
Keep-Alive选项
注意,keep-Alive首部只是请求将连接保持在活跃状态。发出keep-alive请求之后,客户端和服务器并不一定会同意进行keep-alive会话。它们可以在任意时刻关闭空闲的keep-alive连接,并可随意限制keep-alive连接所处理事务的数量。
可以用Keep-Alive通用首部中指定的、由逗号分隔的选项来调节keep-alive的行为。
- 参数timeout是在Keep-Alive响应首部发送的。它估计了服务器希望将连接保持在活跃状态的时间。这并不是一个承诺值。
- 参数max是在Keep-Alive响应首部发送的。它估计了服务器还希望为多少个事务保持此连接的活跃状态。这并不是一个承诺值。
- Keep-Alive首部还可支持任意未经处理的属性,这些属性主要用于诊断和调试。语法为name [=value]。
Keep-Alive首部完全是可选的,但只有在提供Connection: Keep-Alive时才能使用它。这里有个Keep-Alive响应首部的例子,这个例子说明服务器最多还会为另外5个事务保持连接的打开状态,或者将打开状态保持到连接空闲了2分钟之后。
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
Keep-Alive连接的限制和规则
使用keep-alive连接时有一些限制和一些需要澄清的地方。
- 在HTTP/1.0中,keep-alive并不是默认使用的。客户端必须发送一个Connection: Keep-Alive请求首部来激活keep-alive连接。
- Connection: Keep-Alive首部必须随所有希望保持持久连接的报文一起发送。如果客户端没有发送Connection: Keep-Alive首部,服务器就会在那条请求之后关闭连接。
- 通过检测响应中是否包含Connection: Keep-Alive响应首部,客户端可以判断服务器是否会在发出响应之后关闭连接。
- 只有在无需检测到连接的关闭即可确定报文实体主体部分长度的情况下,才能将连接保持在打开状态——也就是说实体的主体部分必须有正确的Content-Length,有多部件媒体类型,或者用分块传输编码的方式进行了编码。在一条keep-alive信道中回送错误的Content-Length是很糟糕的事,这样的话,事务处理的另一端就无法精确地检测出一条报文的结束和另一条报文的开始了。
- 代理和网关必须执行Connection首部的规则。代理或网关必须在将报文转发出去或将其高速缓存之前,删除在Connection首部中命名的所有首部字段以及Connection首部自身。
- 严格来说,不应该与无法确定是否支持Connection首部的代理服务器建立keep-alive连接,以防止出现下面要介绍的哑代理问题。在实际应用中不是总能做到这一点的。
- 从技术上来讲,应该忽略所有来自HTTP/1.0设备的Connection首部字段(包括Connection: Keep-Alive),因为它们可能是由比较老的代理服务器误转发的。但实际上,尽管可能会有在老代理上挂起的危险,有些客户端和服务器还是会违反这条规则。
- 除非重复发送请求会产生其他一些副作用,否则如果在客户端收到完整的响应之前连接就关闭了,客户端就一定要做好重试请求的准备。
Keep-Alive和哑代理
Web客户端的Connection: Keep-Alive首部应该只会对这条离开客户端的TCP链路产生影响。这就是将其称作“连接”首部的原因。如果客户端正在与一台Web服务器对话,客户端可以发送一个Connection: Keep-Alive首部来告知服务器它希望保持连接的活跃状态。如果服务器支持keep-alive,就回送一个Connection: Keep-Alive首部,否则就不回送。
现代的代理都绝不能转发Connection首部和所有名字出现在Connection值中的首部。因此,如果一个代理收到了一个Connection:Keep-Alive首部,是不应该转发Connection首部,或所有名为Keep-Alive的首部的。
另外,还有几个不能作为Connection首部值列出,也不能被代理转发或作为缓存响应使用的首部。其中包括Proxy-Authenticate、Proxy-Connection、Transfer-Encoding和Upgrade。
持久连接的限制和规则
在持久连接的使用中有以下限制和需要澄清的问题。
- 发送了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条到任意服务器或父代理的连接。
总结
在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)