应用层HTTP也不简单——HTTP连接管理

HTTP的连接管理

谈起HTTP协议,我们知道现在HTTP已有诸多个版本(1.0、1,1、2.0、3.0)。其中,平常应用的比较多的便是HTTP /1.1和/2.0,由于3.0还比较新,本篇就不涉及了。

而处于应用层的HTTP要如何优化才能使其在通信中提高效率的同时保证安全,就涉及到我们对HTTP连接的管理了,本篇我们就来讲一讲HTTP主要的几种连接管理模型。

HTTP连接之前

在了解HTTP的连接管理以及相应的性能表现之前,我们必须知道的是,HTTP作为应用层的协议,是非常依赖于传输层TCP协议的,因此无论连接管理模型为何,性能表现、安全水平等指标都取决于TCP。

同时必须注意,HTTP本身没有连接这一说 ,这里说的连接都是TCP连接,本文所要讨论的连接管理,也更多的是指在连接之上如何发送请求对TCP连接进行管理。

针对这一点,本文会穿插进TCP连接的知识,从TCP的角度分析问题,这对我们理解HTTP的连接管理是很有帮助的。

并且,对于一个完整的请求处理(请求发送+响应确认),我们引用数据库并发控制的概念,认为这是一个完整的事务,是不可分割的。

HTTP 短连接的不足

我们知道,HTTP/1.0默认的连接方式是短连接 ------在一个TCP连接中只能处理一次事务 。每请求一个文档,不仅需要单独建立对应的TCP连接花费开销(时间、缓存和变量),还需要两倍的RTT(Return Trip Time往返时延)。当万维网上客户量比较大的时候,这无疑会使服务器难以负担。这样的话,用户想要获取一个站点的完整的资源(比如网站),就必须多次建立TCP连接并发送多次请求,才能完成------这样的效率是非常低的。

这样的短链接又被称为非持续连接,串行连接。

实际上,TCP本身具备热连接的能力,但是HTTP 短连接的机制没能让其发挥,不能够减轻减少连接建立和断开的开销、提高通信的效率。

热连接(Hot):即在一段时间内保持持久的TCP连接。

稍微一提,对于每一条新建立的TCP连接,我们都会存在连接时延慢启动时延。如果我们一直采取HTTP短连接,互联网的规模那么庞大,发送的请求总数也必然是巨大的,这样给每一位造成的时延就几乎是龟速了。

连接时延:可以简单理解成一条TCP连接的初始化。

慢启动时延:为了适应对方的接收窗口而产生的时延。

计算机网络发展至今,如今已有多种方案可以提高HTTP的连接性能。接下来我们主要介绍四种连接管理模型(技术):

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

HTTP并行连接 多条连接多个事务

首先必须认识到,不管是哪个HTTP版本,想要获取某个资源,就只能单独发送一次HTTP请求,因为在每一份HTTP报文当中只能有一个URL。那既然无法在请求上做出优化,我们便只能在连接上下功夫、做优化了。

简单粗暴的一种方法是,在同一个时间段内,直接创建多条连接,并行地执行多个HTTP事务,即每个事务单独对应一条连接。

这似乎可以提高页面的加载速度。只要因特网的带宽足够,并行的方式不仅能够提高加载速度,还可以提高带宽的利用率。

但问题也出在这里。倘若真的每一个事务都开一条连接,就像我们之前讨论短连接的那样,完整加载一个网页就要开一百多个连接,这样很容易就会耗尽带宽资源 ,此时事务之间可能还会为了有限的带宽产生竞争------每个事务的处理速度就更慢了,还不如短连接呢。

从性能的角度来看,问题也很明显。TCP连接本身就很复杂,这种复杂不仅体现在TCP报文首部的构成 ,也体现在TCP连接为了保证可靠性和效率所执行的一系列机制。本篇重点不在TCP,因此只给出体现出TCP知识大纲的图片。

那么既然维持一条TCP连接,就必须有相应的控制程序,因而也必然会占用相当一部分内存。回到本小节举的例子,服务器要为一个用户创建数百条连接,且为一条连接都得开辟独立的内存(无论这条连接所负责的事务是大是小)------而通常,Web服务器要处理很多用户的请求,这无疑会造成服务器性能的严重下降。对高负荷的代理来说也是如此。

这没有解决问题,还是短连接的毛病。

但是这样的并行,从用户的角度来看,好像真的变得更快了------草率地来看,同一时段处理多件事,就是在一个时段傻傻地只做一件事而其他工作全部搁置,要快一些。

但即便如此,我们也并不是就此弃用了并行连接,而是对并行进行了限制,即将并行的连接总数限制为一个较小的值(通常是4个),而且服务器也可以随意关闭来自特定客户端的超量连接。

HTTP 持久连接 一条连接多次事务

也叫长连接、持久连接

从本质上,并行连接的作用原理和短连接是差不多的,毛病的原因也是相同的:一条连接只处理一个事务 。既然如此,为什么不尝试一下一条连接处理多个事务呢?

HTTP当中也确实有这样的连接管理,我们将其称之为持久连接

持久连接热连接 最大的不同,就是二者的协议层次不同。长连接是应用层 的概念,是一种高层次的连接,主要应用在HTTP服务中;而热连接是传输层的,比长连接要更加通用

持久连接 会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。重用 已对目标服务器打开的空闲持久连接,就可以避免缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动造成的时延,以便于我们更快速地进行数据传输。

持久连接分为两类:**HTTP/1.0+"keep-alive"连接和HTTP/1.1 "persistent"**连接。我们分开讲一讲。

HTTP/1.0+keep-alive连接

其实早在1996年的时候,持久连接就已经出现了。因为HTTP/1.0默认是短连接,需要设置请求报文的首部字段Connection值为Keep-Alive ,并且服务器同意该请求 之后才能将一条连接保持在打开状态,因此这样的持久连接称之为keep-alive连接,是一种早期实验型的持久连接。

TCP连接都是双向/全双工的,通信双方可以发出请求,也必须能够接收到响应。因此我们接着刚才所讲,发出keep-alive请求不一定能将连接设置为持久连接(或者说,保持在活跃状态),只有接收到了对方的**同意(即对方也发送一份包含有keep-alive的响应报文)**之后才可以真正建立。并且由于HTTP/1.0默认都是短连接和keep-alive连接本身的限制,在请求发送并获得同意之后,也绝对不意味着一劳永逸。

**在keep-alive连接刚建立时,之后通信双方发给对方的每一份报文首部Connection字段都必须有Keep-Alive,否则对方受到请求后就会以为现在要关闭连接。**用更加官方的话来讲就是:Connection:Keep-Alive首部必须随所有希望保持持久连接的报文一起发送。

  • 对于客户端,如果它没有发送Connection:Keep-Alive首部,服务器就会在那条请求之后关闭连接。
  • 对于服务器,它没有在响应中包含Connection:Keep-Alive首部,客户端就会在那条响应之后关闭连接。

有同学可能会注意到,Keep-Alive通用首部有两个选项值maxtimeout可以调节keep-alive的行为,但这两个都是希望值,没有实际的作用。

  1. Timeout选项:表示服务器愿意在没有收到进一步请求的情况下保持连接的时间。这个选项值并不是一个确切的承诺,而是服务器建议的时间范围。实际的保持连接时间取决于服务器的策略和配置。
  2. Max选项:表示服务器愿意在单个连接上处理的最大请求数。同样,这个选项值也不是一个确切的承诺,而是服务器建议的数量。实际的最大请求数取决于服务器的配置和限制。

Keep-Alive和哑代理

其实如果只是在每个报文都加上keep-alive的话,我们一直使用HTTP/1.0 keep-alive持久连接好像也没什么不方便。但在客户端和服务器的通信过程中还会出现这样一个问题,那就是通信过程中的代理可能无法理解Connection首部。

在网络当中存在很多老旧或简单的代理对待发来和发出的网络包,都只是起一个中继的作用------只负责将字节从一个连接转发到另一个连接中去,不对Connection首部进行特殊的处理。这样一来的话,本就属于逐跳首部Connection字段以及包括它的值就会原封不动地发送到另一个连接------这会引发一个称作哑代理的问题。

逐跳标头(Hop-by-hop):这类标头仅对单次传输连接有意义,并且不得由代理重传或者缓存 。注意,只能使用Connection标头来设置逐跳标头。

这里我们直接引用《HTTP权威指南》当中的一幅图来解释,我觉得这幅图非常的直观:

  1. 首先客户端向服务器请求建立一条keep-alive连接,不过客户端必须先经过一个代理。
  2. 代理虽然接收到了这条HTTP请求,但是它根本不理解Connection首部,不知道该如何处理,于是将其原封不动地带着报文一同交付给了服务器。
  3. 服务器接收到了这条请求建立keep-alive连接的报文,但是在服务器看来,这是代理发来的请求 (这也是逐跳首部的特点。在建立连接这一点上,代理、客户端和服务器没有严格的区分界限,服务器把代理看作是客户端是很正常的),那么此时服务器就误以为代理想要和它建立keep-alive连接 ,而代理连Connection首部都理解不了,怎么建立的起来呢?!
  4. 服务器可不知道马上要与之进行keep-alive对话的客户端是个连字都不认识的小毛孩,这就同意了此次keep-alive请求。接下来的工作,服务器会按照keep-alive的规则进行。
  5. 代理收到了同意报文,同样,还是不对Connection首部做任何处理,直接回送了客户端。
  6. 客户端很高兴,以为服务器同意了和它进行keep-alive通话。此时,客户端和服务器都产生了误解,都以为自己正在和服务器/客户端进行keep-alive通信,但实际上连接的另一端都是大字不识的代理。
  7. 为什么这样就会出大问题呢?因为对于代理来说,正是因为它的不作为,keep-alive连接根本就没有建立成功
    • 代理不能处理Connection首部,也就无法识别keep-alive,因此建立的连接全都是短连接,处理完一个事务,立刻就要关闭。
    • 但是服务器认为代理想要建立keep-alive连接,客户端认为服务器同意了建立keep-alive连接,它俩都将按照keep-alive的规则运作。这和作为中继的代理就产生了不一致。
  8. 接下来只要代理为客户端处理完一个事务,代理负责的两条连接(连接到客户端的连接1,连接到服务器的连接2)都应该断开。代理会请求关闭连接(TCP连接释放也需要请求)
  9. 但就如刚才所说的,客户端和服务器都会按照keep-alive连接方式去工作。代理会一直等待连接释放的响应,也不会继续处理客户端发来的其他报文。浏览器就一直这样挂着,直到连接超时。

这就是哑代理 问题,而其中无法处理Connection首部的代理称为盲中继

使用Proxy-Connection仍然无法解决问题

从上述的讲解当中,我们不难发现哑代理问题的根源是转发了逐跳首部并且认错了人 ------逐跳首部只与一条特定的连接有关,不能被转发,且当下游服务器误将转发来的首部来作为代理自身的请求解释,并用来控制自己的连接时,就会引发问题。

Netscape公司为了解决这样的问题,曾想出一种方法:设置浏览器会向代理发送非标准的Proxy-Connection扩展首部而不是官方支持的Connection首部。这样,即便盲中继不加处理地将Proxy-Connection首部转发给了Web服务器,服务器也会因为不认可该首部而选择忽略,也许持久连接不能被建立,也至少不会出现浏览器持续挂起的糟糕状况。

而如果我们的代理能够理解持久连接(这样的代理我们就称之为聪明的代理 ),就会用一个Connection首部来取代无意义的Proxy-Connection首部,并将其转发给服务器,达成建立持久连接的目的。

然而,这只适用于客户端和服务器之间只存在一个盲中继的情况。只要聪明的代理一侧有盲中继,我们就又会遭遇哑代理的问题。这里还是给出《HTTP 权威指南》的一张图,如果上一小节关于哑代理的陈述都能理解的话,这里的问题也应该很容易看得出来。

HTTP/1.1 持久连接

认识到HTTP/1.0 +keep-alive持久连接存在的问题后,我们再来学习HTTP/1.1的持久连接,就知道HTTP/1.1 持久连接的一些设定是为什么了。

与HTTP/1.0 +keep-alive连接不同的第一点就是,HTTP/1.1 默认状态都是持久连接,不必要在之后的每个报文首部都添加持久连接的字段提示。想要关闭的话,就必须在报文中显式地添加一个Connection:close首部,不发送,就一直保持着持久连接的状态。并且,这样的关闭是不需要响应的,HTTP/1.1 设备可以在任意时刻关闭连接。------比HTTP/1.0更为灵活,也比较安全。

对于HTTP/1.0的哑代理问题,HTTP/1.1的设定是:

  1. HTTP/1.1的代理必须能够分别管理与客户端和服务器的持久连接------每个持久连接都只适用于一跳传输。
  2. HTTP/1.1的代理服务器不应该与HTTP/1.0客户端建立持久连接。

目前我们使用的持久连接都是HTTP/1.1 持久连接。然而,在当今的互联网,还有相当一部分仍在使用HTTP/1.0+keep-alive连接,并且代理也有一部分是老旧的、会违反规定转发Connection首部的代理。因此,HTTP的实现者应该做好与之进行交互操作的准备

顺道一说,当前,很多Web应用程序都会将并行连接持久连接 结合起来使用,即创建少量并行连接,而每一条连接都是持久连接

HTTP 1.1 流水线模型

也称作管道化连接。

这是基于HTTP/1.1 持久连接之上的又一种性能更加优秀的连接管理模型。

在一般的事务当中,下一个的请求的发送必须等到上一个请求的响应被受到后才能发送。但是流水线模型可就不一样了------当第一个请求发往服务器的过程中,不必等待响应,第二天第三条请求就可以开始发送。这在高时延的网络条件下,可以明显降低网络的环回时间,提高性能。

不过,依据官方文档,流水线模型想要使用也是有限制的:

  1. 必须是基于持久连接的。
  2. 必须按照与请求相同的顺序回送HTTP响应
  3. HTTP客户端必须做好连接会在任意时刻关闭的准备,且应当准备好重发在流水线中所有未完成的请求。
  4. HTTP客户端不应该用流水线模型发送会产生副作用的请求(比如非幂等的POST请求)

POST在设计上被认为是非幂等的,即多次重复发送相同的POST请求可能会导致不同的结果或产生不同的影响------在这个前提下,系统擅自重发请求很有可能对数据进行多次修改,而对用户而言动作只希望执行一次。举个例子,倘若在一个电商网站上,用户购买这一动作属于流水线模型的POST请求,这时网络发生故障/流水线关闭,POST请求还没有完成,系统擅自重发,可能会造成用户多次购买的结果!

相反,幂等的请求方法(如GET)不会对服务器状态产生影响,多次重复发送相同的请求得到的结果都是一样的。------这时,重发对用户也不会有什么坏处,至少不会在不知情的情况下下单了好几次......

HTTP/2.0 多路复用

来到HTTP/2,我们对每一个报文处理的颗粒度更小了,在HTTP/1.1之上又做出了优化。由于当今我们最常使用的是HTTP/1.1,对HTTP/2.0的应用较少,因此这里只简单说明一下HTTP/2.0 连接管理机制之一的分路复用机制。

HTTP/2.0 多路复用同样允许在单个TCP连接上以为单位同时传输多个HTTP请求和响应,解决了HTTP/1.x中的串行传输和队头阻塞问题,提高了性能和效率。不过相比于之前的HTTP 连接管理模型,HTTP/2.0还有以下的特点:

  1. 报文分解成帧(Frame) :HTTP/2将请求和响应划分为多个帧,每个帧都有自己的标识和优先级。帧可以同时在单个TCP连接上发送和接收,允许多个请求和响应同时进行。
  2. 流工作模式(Stream) :HTTP/2通过流的概念来管理和标识多个请求和响应。每个流都有唯一的标识符(Stream ID),用于区分不同的请求和响应。在一个TCP连接上可以同时存在多个流,它们可以乱序发送和接收。
  3. 头部压缩(Header Compression) :HTTP/2使用HPACK算法对请求和响应的头部进行压缩。这样可以减小头部的大小,减少网络传输的数据量。头部压缩是在客户端和服务器之间共享的,通过维护一个动态的头部表,避免了重复的头部信息传输。
  4. 优先级(Priority) :HTTP/2允许为每个流设置优先级,以指定其重要性和处理顺序 。通过优先级设置,可以确保重要的请求优先得到处理,避免低优先级请求被阻塞
  5. 并发处理(Concurrency) :由于多路复用的特性,HTTP/2可以并发处理多个请求和响应。服务器可以同时发送多个响应,而客户端可以同时发送多个请求,而无需等待前一个请求的响应。这显著提高了性能和效率。
  6. 服务器推送(Server Push):HTTP/2还引入了服务器推送的功能,允许服务器在客户端请求之前主动推送相关资源。服务器可以根据页面内容或预测算法主动推送与请求相关的资源,减少客户端的请求次数和延迟。

HTTP/1.x 域名分片

这是从MDN资料中翻阅出来的一个概念,和HTTP并行连接机理是相似的。即请求一个站点的资源时,为这个站点的域名拆分成多个域名,并让这些域名都指向同一台服务器,浏览器就会同时为每个域名分别建立连接。这里也可以采用并行连接,一个域名建立多条连接。

假设有个域名www.example.com,我们可以把它拆分成好几个域名:www1.example.comwww2.example.comwww3.example.com。所有这些域名都指向同一台服务器,假设浏览器会同时为每个域名建立2条连接,那么现在我们的连接总数就有6条了。

可以看出,这和HTTP并行连接大同小异。实际上,域名分片已经时一门过时的技术了。在 HTTP/2 里,做域名分片就没必要了:HTTP/2 的连接可以很好的处理并发的无优先级的请求。域名分片甚至会影响性能。大多数 HTTP/2 的实现还会使用一种称作连接聚合的技术去尝试合并被分片的域名。

参考资料

  1. [图灵指南]《HTTP权威指南》

  2. HTTP/1.x 的连接管理 - HTTP | MDN (mozilla.org)

相关推荐
njnu@liyong9 小时前
图解HTTP-HTTP报文
网络协议·计算机网络·http
ZachOn1y10 小时前
计算机网络:应用层 —— 应用层概述
计算机网络·http·https·应用层·dns
Graceful_scenery20 小时前
https双向认证
服务器·网络·网络协议·http·https
njnu@liyong1 天前
图解HTTP-HTTP状态码
网络协议·计算机网络·http
代码洁癖症患者1 天前
HTTP请求的奇幻旅程:从发起至响应的全方位探索
网络·网络协议·http
寻找沙漠的人2 天前
HTTP—02
网络·网络协议·http
范紫涵-19期-工职大2 天前
前端HTTP协议传输以及背后的原理总结
网络·网络协议·http
龙少95432 天前
【Http,Netty,Socket,WebSocket的应用场景和区别】
java·后端·websocket·网络协议·http
小林熬夜学编程2 天前
【Linux网络编程】第十三弹---构建HTTP响应与请求处理系统:从HttpResponse到HttpServer的实战
linux·运维·服务器·c语言·网络·c++·http
旷野..2 天前
为什么 HTTP/3 抛弃了 TCP?是解决问题还是制造问题
tcp/ip·http·制造