HTTP的前世今生
HTTP是全球最大规模的分布式系统网络的基础之一,也采用了传统的服务器-客户端的通信设计模式。从1.0版本到1.1版本再到2.0版本,HTTP始终占据着分布式系统通信领域重要的一席之地。
HTTP的设计思路
首先,在报文编码方式上,HTTP采用了面向程序员的文本((ASCII)编码方式而非面向计算机的二进制编码方式。该设计非常关键,这是因为文本编码数据很直观,文本编码协议甚至不用编写额外冗长的接口说明文档就很容易被程序员理解,也非常方便我们准备模拟数据编写单元测试,而当线上系统出现 Bug时,运维人员也很容易根据客户端记录的文本报文日志来快速定位故障。文本编码协议正因为有这么多优点,所以始终在网络协议中占据着重要的位置,而很多复杂的分布式系统可能会同时采用文本与二进制这两种编码方式的协议。
其次,HTTP是无状态的请求-应答协议。在笔者看来,无状态的设计是个严重缺乏前瞻性的设计,但考虑到在HTTP诞生之初网上没什么资源,也根本不存在可以跟用户交互的网站,因此这个设计思路也是完全可以理解的。最初的HTTP (0.9版)只提供了GET方法,这是因为其作者认为网上所有的资源(网页)都是静态的,远程用户是不能修改的,浏览器所能做的就是从远程服务器上“获取(GET)”指定网页并以只读方式展示给用户,在用户获取网页之后就立即中断与服务器的连接,从而节省宽带和服务器的宝贵资源。
随着Internet的加速发展,特别是图片和音视频等多媒体内容的出现和流行,原先只面向文本资源对象的HTTP已不能满足人们的需求,所以HTTP做了一个较大的升级(1.0版本):首先,增加了POST方法,使得客户端可以提交(上传)文件到服务器端;其次,通过引入Content-Type这个Header,支持除文本外的多媒体数据的传输支持。需要注意的是,此时在HTTP的报文里是可以出现二进制数据的,比如文件附件,但从整体来看,HTTP报文仍然是文本协议的报文,只是在报文的尾部可以增加一些二进制编码数据。在增加POST方法并且支持文件上传功能之后,在HTTP里出现了一个概率Bug 的设计。原本的问题如下:如果用户通过POST方式可以上传多个文件,那么我们应该怎么设计HTTP来支持它?”
一个非计算机系的人面对这个问题,可能会这样考虑:既然HTTP一开始是没有考虑二进制传输的,那么现在的确存在二进制传输这种新的需求,所以我们应该考虑如何引入新的二进制传输协议来支持此需求,比如文件传输的数据可以用[文件名][长度][文件内容]这样的二进制编码格式定义,就很容易支持多个文件传输。
但对于典型的“IT直男”来说,上面这种突变的设计与之前的设计格格不入,直接违背了他们遵循的一致性审美原则,同时增加了代码实现的复杂度,这就很难让人接受了。所以,他们把电子邮件协议(SMTP&Pop3)中处理二机制附件的做法照搬了过来。电子邮件协议采用的是文本协议,用一个随机生成的boundary字符串来区分多个文件(附件)的数据。这个boundary字符串虽然是随机生成的,也有一定长度,但谁也无法保证它永远不会跟文件内容中的一段字符串重复,这就导致了随机 Bug 的问题。很有意思的是,当初制定电子邮件协议的人们也从程序逻辑思维的角度制定出来一个无限层附件嵌套附件的协议规范。笔者当初开发Java版的邮件服务器时,特意模拟过一个3层嵌套的电子邮件,结果让163等常见的Web Mail都挂了,因为其无法识别嵌套的邮件附件。
HTTP在1.0版本中引入了一个重要的设计,即在报文中增加了Header属性列表,每个Header都是一个Key/Value键值对,整个Header列表可以被视为一个 Map的数据结构,用来在客户端(浏览器)与服务器端传递控制类数据。由于Header 与请求或应答的正文内容相互独立,并且用户可以灵活扩展,增加新的Header属性,同时这些Header数据会被HTTP代理服务器透传到远程服务器中,所以用HTTP构建分布式系统具有其他应用层协议没有的独特优势。HTTP最大的优势可以一句话概括为:采用了HTTP作为通信协议的分布式系统天然具备了无侵入性的基础设施能力全面改进的优势。
上述优势使得HTTP在大规模的分布式系统,特别是目前越来越热的云原生系统中得到应用。随着HTTP 2.0的进一步升级和发展,基于HTTP 2.0的微服务架构、服务网格风起云涌。所以理解HTTP,有助于我们深入理解常见的分布式系统架构的设计与原理实现。
HTTP 如何保持状态
我们都知道,HTTP在设计之初就是无状态的协议,但随着互联网的快速发展,越来越多的软件开始以 Web网站的方式提供服务,一个 Web网站同时服务成千上万个互联网用户。此时,编程人员开始面对一个棘手的问题,即如何识别同一个用户的连续多次的请求?比如在典型的网购行为中,客户登录系统,挑选商品,将商品添加购物车,最后下单付款。一个客户网购的整个过程会涉及几十次甚至上百次的网页交互,这就意味着我们必须为无状态的HTTP引入某种状态机制,而具体的实现机制就是HTTP Cookie。
HTTP Cookie新增了两个扩展性的HTTP Header,其中一个是Set-Cookie。
Set-Cookie是服务端专用的 Header,用来告