漫谈 HTTP 链接

图片

本文首先会 HTTP 的特色和优缺点,而后会详细介绍 HTTP 长链接和短链接的链接管理,经过阅读本文可以对 HTTP 链接有个深刻的认识。图片经过前面的 HTTP 系列文章,想必你们已经知道 HTTP 协议的基本知识,了解它的报文结构,请求头、响应头等细节。html

HTTP 的特色

因此接下来先是聊聊 HTTP 协议的特色、优势和缺点。既要看到它好的一面,也要正视它很差的一面,只有全方位、多角度了解 HTTP,才能实现“扬长避短”,更好地利用 HTTP。后端

灵活可扩展浏览器

首先, HTTP 协议是一个“灵活可扩展”的传输协议。缓存

HTTP 协议最初诞生的时候就比较简单,本着开放的精神只规定了报文的基本格式,好比用空格分隔单词,用换行分隔字段,“header+body”等,报文里的各个组成部分都没有作严格的语法语义限制,能够由开发者任意定制。性能优化

因此,HTTP 协议就随着互联网的发展一同成长起来了。在这个过程当中,HTTP 协议逐渐增长了请求方法、版本号、状态码、头字段等特性。而 body 也再也不限于文本形式的 TXT 或 HTML,而是可以传输图片、音频视频等任意数据,这些都是源于它的“灵活可扩展”的特色。服务器

而那些 RFC 文档,实际上也能够理解为是对已有扩展的“认可和标准化”,实现了“从实践中来,到实践中去”的良性循环。网络

也正是由于这个特色,HTTP 才能在三十年的历史长河中“屹立不倒”,从最初的低速实验网络发展到如今的遍及全球的高速互联网,始终保持着旺盛的生命力。数据结构

可靠传输架构

第二个特色, HTTP 协议是一个“可靠”的传输协议。并发

这个特色显而易见,由于 HTTP 协议是基于 TCP/IP 的,而 TCP 自己是一个“可靠”的传输协议,因此 HTTP 天然也就继承了这个特性,可以在请求方和应答方之间“可靠”地传输数据。

它的具体作法与 TCP/UDP 差很少,都是对实际传输的数据(entity)作了一层包装,加上一个头,而后调用 Socket API,经过 TCP/IP 协议栈发送或者接收。

不过咱们必须正确地理解“可靠”的含义,HTTP 并不能 100% 保证数据必定可以发送到另外一端,在网络繁忙、链接质量差等恶劣的环境下,也有可能收发失败。“可靠”只是向使用者提供了一个“承诺”,会在下层用多种手段“尽可能”保证数据的完整送达。

固然,若是遇到光纤被意外挖断这样的极端状况,即便是神仙也不能发送成功。因此,“可靠”传输是指在网络基本正常的状况下数据收发一定成功,借用运维里的术语,大概就是“3 个 9”或者“4 个 9”的程度吧。

应用层协议

第三个特色,HTTP 协议是一个应用层的协议。

这个特色也是不言自明的,但却很重要。

在 TCP/IP 诞生后的几十年里,虽然出现了许多的应用层协议,但它们都仅关注很小的应用领域,局限在不多的应用场景。例如 FTP 只能传输文件、SMTP 只能发送邮件、SSH 只能远程登陆等,在通用的数据传输方面“彻底不能打”。

因此 HTTP 凭借着可携带任意头字段和实体数据的报文结构,以及链接控制、缓存代理等方便易用的特性,一出现就“技压群雄”,迅速成为了应用层里的“明星”协议。只要不太苛求性能,HTTP 几乎能够传递一切东西,知足各类需求,称得上是一个“万能”的协议。

套用一个网上流行的段子,HTTP 彻底能够用开玩笑的口吻说:“不要误会,我不是针对 FTP,我是说在座的应用层各位,都是垃圾。”

请求 - 应答

第四个特色,HTTP 协议使用的是请求 - 应答通讯模式。

这个请求 - 应答模式是 HTTP 协议最根本的通讯模型,通俗来说就是“一发一收”“有来有去”,就像是写代码时的函数调用,只要填好请求头里的字段,“调用”后就会收到答复。

请求 - 应答模式也明确了 HTTP 协议里通讯双方的定位,永远是请求方先发起链接和请求,是主动的,而应答方只有在收到请求后才能答复,是被动的,若是没有请求时不会有任何动做。

固然,请求方和应答方的角色也不是绝对的,在浏览器 - 服务器的场景里,一般服务器都是应答方,但若是将它用做代理链接后端服务器,那么它就可能同时扮演请求方和应答方的角色。

HTTP 的请求 - 应答模式也刚好契合了传统的 C/S(Client/Server)系统架构,请求方做为客户端、应答方做为服务器。因此,随着互联网的发展就出现了 B/S(Browser/Server)架构,用轻量级的浏览器代替笨重的客户端应用,实现零维护的“瘦”客户端,而服务器则摈弃私有通讯协议转而使用 HTTP 协议。

此外,请求 - 应答模式也彻底符合 RPC(Remote Procedure Call)的工做模式,能够把 HTTP 请求处理封装成远程函数调用,致使了 WebService、RESTful 和 gPRC 等的出现。

无状态

第五个特色,HTTP 协议是无状态的。这个所谓的“状态”应该怎么理解呢?

“状态”其实就是客户端或者服务器里保存的一些数据或者标志,记录了通讯过程当中的一些变化信息。

你必定知道,TCP 协议是有状态的,一开始处于 CLOSED 状态,链接成功后是 ESTABLISHED 状态,断开链接后是 FIN-WAIT 状态,最后又是 CLOSED 状态。

这些“状态”就须要 TCP 在内部用一些数据结构去维护,能够简单地想象成是个标志量,标记当前所处的状态,例如 0 是 CLOSED,2 是 ESTABLISHED 等等。

再来看 HTTP,那么对比一下 TCP 就看出来了,在整个协议里没有规定任何的“状态”,客户端和服务器永远是处在一种“无知”的状态。创建链接前二者互不知情,每次收发的报文也都是互相独立的,没有任何的联系。收发报文也不会对客户端或服务器产生任何影响,链接后也不会要求保存任何信息。

“无状态”形象地来讲就是“没有记忆能力”。好比,浏览器发了一个请求,说“我是小明,请给我 A 文件。”,服务器收到报文后就会检查一下权限,看小明确实能够访问 A 文件,因而把文件发回给浏览器。接着浏览器还想要 B 文件,但服务器不会记录刚才的请求状态,不知道第二个请求和第一个请求是同一个浏览器发来的,因此浏览器必须还得重复一次本身的身份才行:“我是刚才的小明,请再给我 B 文件。”

咱们能够再对比一下 UDP 协议,不过它是无链接也无状态的,顺序发包乱序收包,数据包发出去后就无论了,收到后也不会顺序整理。而 HTTP 是有链接无状态,顺序发包顺序收包,按照收发的顺序管理报文。

但不要忘了 HTTP 是“灵活可扩展”的,虽然标准里没有规定“状态”,但彻底可以在协议的框架里给它“打个补丁”,增长这个特性。

其余特色

除了以上的五大特色,其实 HTTP 协议还能够列出很是多的特色,例如传输的实体数据可缓存可压缩、可分段获取数据、支持身份认证、支持国际化语言等。但这些并不能算是 HTTP 的基本特色,由于这都是由第一个“灵活可扩展”的特色所衍生出来的。

小结

  • HTTP 是灵活可扩展的,能够任意添加头字段实现任意功能;
  • HTTP 是可靠传输协议,基于 TCP/IP 协议“尽可能”保证数据的送达;
  • HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,可以传输任意数据;
  • TTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;
  • HTTP 本质上是无状态的,每一个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。

HTTP的链接管理

HTTP 的链接管理也算得上是个“老生常谈”的话题了,你必定曾经据说过“短链接”“长链接”之类的名词,今天让咱们一块儿来把它们弄清楚。

短链接

HTTP 协议最初(0.9/1.0)是个很是简单的协议,通讯过程也采用了简单的“请求 - 应答”方式。

它底层的数据传输基于 TCP/IP,每次发送请求前须要先与服务器创建链接,收到响应报文后会当即关闭链接。

由于客户端与服务器的整个链接过程很短暂,不会与服务器保持长时间的链接状态,因此就被称为“短链接”(short-lived connections)。早期的 HTTP 协议也被称为是“无链接”的协议。

短链接的缺点至关严重,由于在 TCP 协议里,创建链接和关闭链接都是很是“昂贵”的操做。TCP 创建链接要有“三次握手”,发送 3 个数据包,须要 1 个 RTT;关闭链接是“四次挥手”,4 个数据包须要 2 个 RTT。

而 HTTP 的一次简单“请求 - 响应”一般只须要 4 个包,若是不算服务器内部的处理时间,最可能是 2 个 RTT。这么算下来,浪费的时间就是“3÷5=60%”,有三分之二的时间被浪费掉了,传输效率低得惊人。

图片

单纯地从理论上讲,TCP 协议你可能还不太好理解,我就拿打卡考勤机来作个形象的比喻吧。

假设你的公司买了一台打卡机,放在前台,由于这台机器比较贵,因此专门作了一个保护罩盖着它,公司要求每次上下班打卡时都要先打开盖子,打卡后再盖上盖子。

但是恰恰这个盖子很是牢固,打开关闭要费很大力气,打卡可能只要 1 秒钟,而开关盖子却须要四五秒钟,大部分时间都浪费在了毫无心义的开关盖子操做上了。

可想而知,日常还好说,一到上下班的点在打卡机前就会排起长队,每一个人都要重复“开盖 - 打卡 - 关盖”的三个步骤,你说着急不着急。

在这个比喻里,打卡机就至关于服务器,盖子的开关就是 TCP 的链接与关闭,而每一个打卡的人就是 HTTP 请求,很显然,短链接的缺点严重制约了服务器的服务能力,致使它没法处理更多的请求。

长链接

针对短链接暴露出的缺点,HTTP 协议就提出了“长链接”的通讯方式,也叫“持久链接”(persistent connections)、“链接保活”(keep alive)、“链接复用”(connection reuse)。

其实解决办法也很简单,用的就是“成本均摊”的思路,既然 TCP 的链接和关闭很是耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上。

这样虽然不能改善 TCP 的链接效率,但基于“分母效应”,每一个“请求 - 应答”的无效时间就会下降很多,总体传输效率也就提升了。

这里我画了一个短链接与长链接的对比示意图。在短链接里发送了三次 HTTP“请求 - 应答”,每次都会浪费 60% 的 RTT 时间。而在长链接的状况下,一样发送三次请求,由于只在第一次时创建链接,在最后一次时关闭链接,因此浪费率就是“3÷9≈33%”,下降了差很少一半的时间损耗。显然,若是在这个长链接上发送的请求越多,分母就越大,利用率也就越高。

继续用刚才的打卡机的比喻,公司也以为这种反复“开盖 - 打卡 - 关盖”的操做太“反人类”了,因而颁布了新规定,早上打开盖子后就不用关上了,能够自由打卡,到下班后再关上盖子。

这样打卡的效率(即服务能力)就大幅度提高了,原来一次打卡须要五六秒钟,如今只要一秒就能够了,上下班时排长队的景象一去不返,你们都开心。

链接相关的头字段

因为长链接对性能的改善效果很是显著,因此在 HTTP/1.1 中的链接都会默认启用长链接。不须要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 链接,也就是长链接,在这个链接上收发数据。

固然,咱们也能够在请求头里明确地要求使用长链接机制,使用的字段是 Connection,值是 “keep-alive”。

不过无论客户端是否显式要求长链接,若是服务器支持长链接,它总会在响应报文里放一个 “Connection: keep-alive” 字段,告诉客户端:“我是支持长链接的,接下来就用这个 TCP 一直收发数据吧”。

不过长链接也有一些小缺点,问题就出在它的“长”字上。

由于 TCP 链接长时间不关闭,服务器必须在内存里保存它的状态,这就占用了服务器的资源。若是有大量的空闲长链接只连不发,就会很快耗尽服务器的资源,致使服务器没法为真正有须要的用户提供服务。

因此,长链接也须要在恰当的时间关闭,不能永远保持与服务器的链接,这在客户端或者服务器均可以作到。

在客户端,能够在请求头里加上“Connection: close”字段,告诉服务器:“此次通讯后就关闭链接”。服务器看到这个字段,就知道客户端要主动关闭链接,因而在响应报文里也加上这个字段,发送以后就调用 Socket API 关闭 TCP 链接。

服务器端一般不会主动关闭链接,但也可使用一些策略。拿 Nginx 来举例,它有两种方式:

  • 使用“keepalive_timeout”指令,设置长链接的超时时间,若是在一段时间内链接上没有任何数据收发就主动断开链接,避免空闲链接占用系统资源。
  • 使用“keepalive_requests”指令,设置长链接上可发送的最大请求次数。好比设置成 1000,那么当 Nginx 在这个链接上处理了 1000 个请求后,也会主动断开链接。

另外,客户端和服务器均可以在报文里附加通用头字段“Keep-Alive: timeout=value”,限定长链接的超时时间。但这个字段的约束力并不强,通讯的双方可能并不会遵照,因此不太常见。

队头阻塞

看完了短链接和长链接,接下来就要说到著名的“队头阻塞”(Head-of-line blocking,也叫“队首阻塞”)了。

“队头阻塞”与短链接和长链接无关,而是由 HTTP 基本的“请求 - 应答”模型所致使的。

由于 HTTP 规定报文必须是“一发一收”,这就造成了一个先进先出的“串行”队列。队列里的请求没有轻重缓急的优先级,只有入队的前后顺序,排在最前面的请求被最优先处理。

若是队首的请求由于处理的太慢耽误了时间,那么队列里后面的全部请求也不得不跟着一块儿等待,结果就是其余的请求承担了不该有的时间成本。图片仍是用打卡机作个比喻。

上班的时间点上,你们都在排队打卡,可这个时候恰恰最前面的那我的遇到了打卡机故障,怎么也不能打卡成功,急得满头大汗。等找人把打卡机修好,后面排队的全部人全迟到了。

性能优化

由于“请求 - 应答”模型不能变,因此“队头阻塞”问题在 HTTP/1.1 里没法解决,只能缓解,有什么办法呢?

公司里能够再多买几台打卡机放在前台,这样你们能够不用挤在一个队伍里,分散打卡,一个队伍偶尔阻塞也没关系,能够改换到其余不阻塞的队伍。

这在 HTTP 里就是“并发链接”(concurrent connections),也就是同时对一个域名发起多个长链接,用数量来解决质量的问题。

但这种方式也存在缺陷。若是每一个客户端都想本身快,创建不少个链接,用户数×并发数就会是个天文数字。服务器的资源根本就扛不住,或者被服务器认为是恶意攻击,反而会形成“拒绝服务”。

因此,HTTP 协议建议客户端使用并发,但不能“滥用”并发。RFC2616 里明确限制每一个客户端最多并发 2 个链接。不过实践证实这个数字实在是过小了,众多浏览器都“无视”标准,把这个上限提升到了 6~8。后来修订的 RFC7230 也就“顺水推舟”,取消了这个“2”的限制。

但“并发链接”所压榨出的性能也跟不上高速发展的互联网无止境的需求,还有什么别的办法吗?

公司发展的太快了,员工愈来愈多,上下班打卡成了迫在眉睫的大问题。前台空间有限,放不下更多的打卡机了,怎么办?那就多开几个打卡的地方,每一个楼层、办公区的入口也放上三四台打卡机,把人进一步分流,不要都往前台挤。

这个就是“域名分片”(domain sharding)技术,仍是用数量来解决质量的思路。

HTTP 协议和浏览器不是限制并发链接数量吗?好,那我就多开几个域名,好比 shard1.chrono.com、shard2.chrono.com,而这些域名都指向同一台服务器 www.chrono.com,这样实际长链接的数量就又上去了,真是“美滋滋”。不过实在是有点“上有政策,下有对策”的味道。

小结

这一讲中咱们学习了 HTTP 协议里的短链接和长链接,简单小结一下今天的内容:

  • 早期的 HTTP 协议使用短链接,收到响应后就当即关闭链接,效率很低;
  • HTTP/1.1 默认启用长链接,在一个链接上收发多个请求响应,提升了传输效率;
  • 服务器会发送“Connection: keep-alive”字段表示启用了长链接;
  • 报文头里若是有“Connection: close”就意味着长链接即将关闭;
  • 过多的长链接会占用服务器资源,因此服务器会用一些策略有选择地关闭长链接;
  • “队头阻塞”问题会致使性能降低,能够用“并发链接”和“域名分片”技术缓解。

原文:https://www.cnblogs.com/huans...

image

相关文章
相关标签/搜索