从需求角度分析HTTP发展

239 阅读9分钟

人生没有白走的路,每一步它都算数——考研政治老师孔昱力

0. HTTP 是个什么玩应?

他就是个P:HTTP 是一种超文本传输协议(Hypertext Transfer Protocol),说白了就是客户端和服务端之间沟通的一种协议,即二者根据什么规则、按照什么格式进行通信。

先说什么是超文本。计算机发展早期,我们保存的信息通常都以文本即简单字符的形式存在,文本是一种能够被计算机解析的有意义的二进制数据包。而随着互联网的高速发展,两台电脑之间能够进行数据的传输后,人们不满足只能在两台电脑之间传输文字,还想要传输图片、音频、视频,甚至点击文字或图片能够进行超链接的跳转,那么文本的语义就被扩大了,这种语义扩大后的文本就被称为超文本(Hypertext)。

再简单说一下是按照什么格式通信的呀。自HTTP1.0后,浏览器和服务端的通信格式如下: 请求格式的第一行叫做【请求行】

image-20230126192124924

一个get请求的栗子

GET/sample.jsp HTTP/1.1     
Accept:image/gif.image/jpeg    
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
Accept-Encoding:gzip,deflate

username=Anthony&password=1234 

首部以kv形式描述了请求的属性信息和请求体的属性信息,后续会逐渐细说。

1. HTTP1

1.1 HTTP0.9

HTTP0.9是1991年提出来的,他是第一版超文本传输协议。适应当时需求,只有一个简单的GET方法,经过三次握手之后直接就能获得请求的html数据。请求也很是简单,浏览器直接告诉服务器我要哪个哪个文件,没有什么逻辑判断,条件组合啥的。因此,HTTP0.9的特点可以总结如下:

  • 请求格式只有起始行,没有首部和主体。因为一行就足以说明浏览器要哪个文件。
  • 响应格式只有响应体。浏览器要啥就给啥,不需要额外的信息。
  • 返回的文件内容是以ASCII字符流传输的。因为,当时的文件都是html文件,ASCII字节码是最合适的。

1.2 HTTP1.0

时代在发展,社会在进步,需求也变了,于是迎来了HTTP1.0。那HTTP1.0满足了哪些需求,解决了哪些问题呢?

**情景:**此时,人们已经不仅仅满足文字冲浪了,“网民”要看图片,“网民”要听音乐,“网民”要看视频。

需求/问题解决办法
简单的请求响应格式无法满足多种类型文件传输的要求在请求响应格式中加入首部,描述文件属性

图片和视频直接传输太占带宽了,那就先编码、压缩再传输吧。服务端进行了编码、压缩,客户端就需要解码、解压缩,那问题就来了,服务端得告诉客户端采用的什么形式进行的压缩,用的什么字符集进行的编码。客户端也可以在请求时候告诉服务端想要什么样的东西。这些关于请求内容的属性信息就添加到了首部中。首部一部分kv如下

accept: text/html  // 期望服务器返回 html 类型的文件
accept-encoding: gzip, deflate, br // 期望服务器可以采用 gzip、deflate 或者 br 其中的一种压缩方式
accept-Charset: ISO-8859-1,utf-8 // 期望返回的文件编码是 UTF-8 或者 ISO-8859-1
accept-language: zh-CN,zh // 表示期望页面的优先语言是中文

现在请求是发过去了,响应回来后浏览器一脸懵B,响应体是空的。这咋回事,是服务器出问题了还是浏览器请求有问题?马上解决

需求/问题解决办法
浏览器想知道请求的结果引入状态码,放在了起始行中。还引入了原因短语,简短描述请求结果

上边这个问题解决了,可以愉快的向服务器发起请求了。疯狂地请求同样的页面玩玩,于是服务器受不住了。又有问题?马上解决

需求/问题解决办法
对已经请求过的数据还得响应,传输引入缓存,把请求过的放在浏览器本地吧。下次请求,在本地加载,还快呢。

这又得涉及到一堆关于缓存的属性信息。缓存什么时候过期啊等等。后边也会逐渐提到关于缓存的kv。

1.3 HTTP1.1

时代在发展,社会在进步,需求又变了,于是迎来了HTTP1.1。那HTTP1.1满足了哪些需求,解决了哪些问题呢?

那时,通信的文件比较小,而且每个页面的引用也不多,所以这种传输形式没什么大问题。但是随着浏览器普及,单个页面中的图片文件越来越多,有时候一个页面可能包含了几百个外部引用的资源文件。跟服务器请求资源时,每次都要经历建立TCP连接→请求→响应→关闭连接。举个例子,就知道有多麻烦了。

  • 浏览器向服务器请求某个页面,首先建立TCP连接,然后发送请求,服务器给出html响应,然后关闭连接。
  • 浏览器根据html内容请求js文件,首先建立TCP连接,然后发送请求,服务器给出js响应,然后关闭连接。
  • 浏览器根据html内容请求css文件,首先建立TCP连接,然后发送请求,服务器给出css响应,然后关闭连接。

经过上边三步,一个页面就发送进行了三次的TCP连接关闭。

问题出现了,怎么办?马上解决

需求/问题解决办法
每个请求都进行TCP连接断开,太浪费资源了增加长连接。只要浏览器或服务器没人明确断开,就一直连着。就可以进行多次请求响应。

增加了长连接,又得在首部中描述这个东西,表示是否启用长连接。

Connection:Keep-Alive

但是,这一个连接肯定是太慢了,就需要多个连接。不同浏览器支持的连接数是不一样的,比如Chrome支持同时6个连接。即使有多个连接,还是会有问题,那是什么问题呢?还是以请求一个页面为例。假设,在其他连接中hmtl和js文件都收到了,单单只有css文件没收到,浏览器没办法渲染,那其他请求就得等着,等css收到,这就会导致著名的队头阻塞问题。马上解决

需求/问题解决办法
请求分散导致对头阻塞问题引入管线化,把请求整批发送给服务器

虽然,可以一次发送一批请求,但是请求也是有顺序的。而且响应的时候后也必须按照请求的顺序进行响应,浏览器按照请求的顺序进行接收。那网络那么复杂,谁又能保证这个顺序的一致性呢。所以,很少有浏览器会使用管线化技术。

由于HTTP是无状态协议,当我们浏览博客时,看到了哪个位置,下次还想继续接着看,HTTP就无法实现保存这个状态了。需求来了,马上解决

需求/问题解决办法
实现有状态访问服务器引入Cookie,在本地缓存浏览信息,下次请求时带着这个博客的位置信息去。

2. HTTP2.0

还记得上边提到的队头阻塞问题么?管线化技术没能很好解决它。HTTP2.0重点解决的就是对头阻塞问题。管线化因为响应的顺序无法保证,HTTP2.0就引入了多路复用技术,能排序了。

需求/问题解决办法
对头阻塞问题,管线化响应顺序无法保证引入了多路复用技术,能对响应排序了。

现象就是上边说的现象,但是实质却不是那个实质。下边具体说说多路复用技术:

客户端在发送请求时会将每个请求的内容封装成不同的带有编号的二进制帧(Frame),然后将这些帧同时发送给服务端。服务端接收到数据之后,会将相同编号的帧合并为完整的请求信息。同样,服务端返回结果、客户端接收结果也遵循这个帧的拆分与组合的过程。

有了二进制分帧后,对于同一个域,客户端只需要与服务端建立一个连接即可完成通信需求,这种利用一个连接来发送多个请求的方式称为多路复用。每一条路都被称为一个 stream(流)

2015 年正式发布的 HTTP/2 默认不再使用 ASCII 编码传输,而是改为二进制数据,来提升传输效率。

设置请求优先级

刚刚说了能够进行排序了,当然就可以设置优先级了。因为即便是先到的请求因为优先级低被耽误了,也可以在最终响应后根据编号排序。比如接收到 JavaScript 或者 CSS 关键资源的请求,服务器可以暂停之前的请求来优先处理关键资源的请求。

到目前位置几乎引入一个新特性,都在首部中加入了很多kv,导致首部变大,于是问题又来了。马上解决:

需求/问题解决办法
首部kv变大了引入头部压缩。头部=起始行+首部

3. 总结

HTTP0.9

  • 只有GET请求,请求格式只有起始行,响应格式只有响应体。

HTTP1.0

  • 资源格式多了,引入首部描述资源的属性
  • 请求结果一脸懵,引入状态码
  • 解决同一资源多次请求问题,引入缓存机制

HTTP1.1

  • 连接次数太多,引入了长连接
  • 队头阻塞问题,引入管线化
  • 有状态访问,引入cookie

HTTP2.0

  • 对头阻塞问题,管线化无序,引入二进制帧排序
  • 请求优先级
  • 首部kv值太多,太大,引入头部压缩

参考 & 感谢