写点什么

HTTP 前世今生

用户头像
大导演
关注
发布于: 2020 年 11 月 16 日
HTTP 前世今生

HTTP 是什么

HTTPHypertext Transfer Protocol)是客户端与服务器之间的通信语言,早期用来传输超文本内容,故亦被称作超文本传输协议


HTTP/0.9

HTTP/0.9 用来在网络之间传递 HTML 超文本内容,采用请求-响应模式,客户端发起请求,服务器返回数据。以下是 HTTP/0.9 完成一次请求的全过程:


  • 域名解析,获得域名映射的 ip 地址

  • 根据 ip 地址、端口号,建立 TCP 连接(三次握手)

  • 发起请求,请求行 GET /index.html

  • 服务器返回 HTML 文本

  • HTML 传输完成后,断开连接(四次挥手)


特点:

  1. 没有请求头和请求体,只需要一个请求头就可以清楚表达客户端的需求

  2. 没有返回头信息,服务器返回对应的数据即可,不需要告诉客户端太多的信息

  3. 文件内容以 ASCII 字节码传输


HTTP/1.0

拨号上网的出现,多媒体技术的发展以及浏览器的问世,万维网进入高速发展的阶段。在浏览器中展示的不仅仅只是 HTML 文件,还包括 JavaScriptCSS、音视频等不同类型的文件,如何支持多类型文件下载成为 HTTP/1.0 的核心诉求。


要支持多种类型文件的下载,浏览器需要知道服务器返回的数据是什么类型,才能根据对应的类型去解析,为此,HTTP/1.0 引入了请求头和响应头,它们都是以 Key-Value 形式保存,客户端发起请求时会携带请求头信息,服务器返回数据时先返回响应头信息。

RequestHeader

Accept 浏览器端接收的格式

Accept-Encoding 浏览器端接收的编码方式

Accept-Language 浏览器端接收的语言,用于判断服务器多语言

Cache-Control 控制缓存的时效性

Connection 连接方式,如果是 keep-alive,且服务端支持,则会复用连接

Host HTTP 访问使用的域名

If-Modified-Since 上次访问时的更改时间,如果服务端认为此后自己没有更新,则给出 304 响应

If-None-Match 上次访问使用的 E-Tag,通常是页面的信息摘要,这个比更改时间更准确一些

User-Agent 客户端标识

Cookie 客户端存储的 cookie 字符串

……

ResponseHeader

Cache-Control 缓存控制,用于通知各级缓存保存的时间,例如 max-age=0,表示不要缓存

Connection 连接类型,keep-alive 表示复用连接

Content-Encoding 内容编码方式,通常是 gzip

Content-Length 内容的长度,有利于浏览器判断内容是否已经结束

Content-Type 内容类型,所有请求网页的都是 text/html

Date 当前的服务器日期

ETag 页面的信息摘要,用于判断是否需要重新到服务端取回页面,是资源的一个唯一标识

Expires 过期时间,用于判断下次请求是否需要到服务端取回页面

Keep-Alive 保持连接不断时需要的一些信息。例如,timeout=5max=100

Last-Modified 页面上次修改的时间

Server 服务端软件的类型

Set-Cookie 设置 cookie,可以存在多个

Via 服务端的请求链路,对一些调试场景至关重要的一个头

……

StatusCode

有的请求服务器可能无法处理,或者处理出错,这时就需要告知浏览器最终的处理结果,这就引入了状态码


常见状态码如下:

  • 2xx 请求成功

  • 200 OK 请求成功

  • 3xx 请求的资源发生了变动,希望客服端进一步处理

  • 301 Moved Permanently 永久重定向

  • 302 Found 临时重定向

  • 304 Not Modified 缓存重定向

  • 4xx 客户端错误

  • 403 Forbidden 无权限

  • 404 Not Found 请求的资源不存在

  • 405 Method Not Allowed

  • 5xx 服务端错误

  • 500 Internal Server Error 服务端错误

  • 503 Service Unavailable 服务端暂时性错误,可以一会再试


Cache

为了减轻服务器的压力,HTTP/1.0 提供 Cache 机制,用来缓存已经下载过的文件。


HTTP/1.1

HTTP/1.1 有两份标准,分别是:


HTTP/1.1 基于 HTTP/1.0 存在的问题做了一些改进。


增加持久连接


HTTP/1.0 每次请求都会经历建立 TCP 连接、传输数据、断开 TCP 连接三个步骤。如果一个页面引用的外部资源过多,重复这样的步骤,无疑会增加大量无谓的开销。HTTP/1.1 中增加了持久连接的方法,在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,就会一直保持连接。

HTTP/1.1 中默认开启持久连接,Connection: keep-alive


目前浏览器对同一个域名,默认允许同时建立 6 个 TCP 连接,这也是一个性能优化的点。


HTTP 管线化


持久连接虽然减少了 TCP 连接建立和断开的次数,但是需要等到前面的请求响应之后,才能进行下一次请求。如果某个请求没有及时返回,就会阻塞后面的所有请求,这就是著名的队头阻塞


HTTP 管线化是指将多个 HTTP 请求整批提交给服务器,但是服务器依然需要根据浏览器的请求顺序依次响应。FireFoxChrome 都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。

对动态生成的内容提供完美支持


HTTP/1.0 中,需要在响应头中设置 Content-Length,浏览器根据设置的数据大小来接收数据。而动态生成的页面,提前是无法确定文件的大小,导致浏览器不知道何时能接收完数据。HTTP/1.1 引入了 Chunk Transfer 机制,服务器会将数据分割成若干数据块,每个数据块发送时会携带上个数据块的长度,最后使用一个零长度的块作为结束的标志。


Cache


浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效则使用本地缓存;否则向服务器发器请求,并携带缓存标识。根据是否需要向服务器发起 HTTP 请求,将缓存分为:强缓存和协商缓存,强缓存优于协商缓存。


HTTP 缓存都是从第二次请求开始的:


第一次请求资源时,服务器返回资源,并在响应头中回传资源的缓存策略


第二次请求时,浏览器携带请求头信息到服务端验证,命中强缓存直接返回 200。否则就把请求头信息传给服务器。看是否命中协商缓存,命中返回 304;否则服务器会返回新的资源,状态码是 200


HTTP/1.1 Cache


  • 强缓存


服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存;否则执行比较缓存策略。


ExpiresHTTP/1.0 的字段,表示缓存到期时间,是一个绝对时间(服务器当前时间 + 缓存时间),在响应头中设置此字段,告诉浏览器,在缓存未过期之前不需要再次请求。由于是绝对时间,用户可能将本地时间进行修改,导致缓存失效;即使不考虑修改,时差或者误差造成两端时间不一致,也会导致缓存失效。由于上述缺点,HTTP/1.1 中增加了 Cache-control表示资源缓存的最大有效时间,在这个时间段内,客户端不需要向服务端发起请求。两者的区别,前者是绝对时间,后者是相对时间。

Cache-control 的优先级高于 Expires


Cache-control 字段常用的值:


  1. max-age:最大有效时间

  2. must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发起请求,验证资源是否有效

  3. no-cache:不使用强缓存,需要与服务器验证资源是否新鲜

  4. no-store:真正意义上的“不要缓存”,所有内容都不走缓存,包括强制和对比

  5. public:所有的内容都可以缓存(包括客户端和代理服务器,如 CDN 等)

  6. private:所有的内容只有客户端才可以缓存,代理服务器不能缓存,默认值


  • 协商缓存


当浏览器的强缓存失效或者请求头中设置了不走强缓存,并在请求头中设置了 If-Modified-Since 或者 If-None-Match,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中协商缓存,会返回 304 状态,使用浏览器缓存,响应头中会设置 Last-Modified 或者 ETag 字段。


  • HTTP/1.0 If-Modified-Since / Last-Modified


服务器通过 Last-Modified 字段告知浏览器,资源最后依次修改的时间,浏览器将这个值和内容和一起记录在浏览器缓存中,下一次请求相同资源时,浏览器会在请求头中 If-Modified-Since 中写入上一次 Last-Modified 的值,服务器会将两个字段值进行比对,如果相等,表示未作修改,响应 304;否则,表示修改了,响应 200 并返回数据。


无法缓存秒级单位以下的更新;服务器动态生成的文件,Last-Modified 的值永远是文件生成时间,起不到缓存的作用。为解决上述问题,HTTP/1.1 引入了 If-None-Match / ETag


  • HTTP/1.1 If-None-Match / ETag

ETag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器通过 ETag 告知浏览器文件的 hash 标识,下一次浏览器请求资源时,会在请求头 If-None-Match 写入 ETag 的值,与服务器文件的 ETag 进行比较,命中响应 304;否则响应 200 返回新资源


HTTP/1.1 的主要问题:对带宽的利用率不理想。主要有以下 3 个原因


  1. TCP 的慢启动。TCP 连接一旦建立,就开始发送数据,刚开始发送数据的速度比较慢,然后慢慢加快发送数据的速度直到达到一个理想的状态,这个过程就叫慢启动。那 TCP 慢启动会带来哪些问题呢?一般来讲,一个页面的 HTMLCSSJavaScript 文件在 TCP 连接建立之后,就要发起请求,由于慢启动的过程,首次渲染页面的时间变长。

  2. 同时开启多条 TCP 连接,会竞争固定带宽,当带宽不足时,各个 TCP 连接接收数据的速度都会减缓,可能会影响到关键资源的下载速度。

  3. 队头阻塞问题。


基于这这些问题, HTTP/2 是如何解决的呢?

HTTP/2


HTTP/2 的解决思路是:一个域名只使用一个 TCP 长连接和消除队头阻塞,这样一个页面只需要一次慢启动,同时也避免了多个 TCP 连接竞争带宽。


HTTP/2 的核心技术是多路复用,将请求分成一帧一帧的数据去传输,把原来 “Header + Body”的消息打散为二进制帧,用 HEADRS 存放头部数据,DATA 存放实体数据,这种化整为零的思路与 Chunked 分块编码有点像。每一帧都带有请求 ID,通过协议栈将这些帧发送给服务器,服务器接收到所有帧之后,会把请求 ID 相同的帧合并为一条完整的请求,服务端响应,通过二进制分帧层将数据转换为一个个带有请求 ID 的帧,经协议栈发送给浏览器,浏览器根据 ID 编号将帧的数据提交给对应的请求。


HTTP/2 的其他特性


  • 可以设置请求的优先级


  • 服务器推送


HTTP/2 在一定程度上改变了传统的请求-响应工作模式,服务器可以主动向浏览器发送消息。比如,在浏览器请求 HTML 的时候,就附带把页面用到的 CSSJavaScript 文件一并发送给浏览器。


  • 头部压缩


HTTP/2 对请求头和响应头进行了压缩,提升了传输效率。

HTTP/3

持续更新...


参考


  • 浏览器工作原理与实践

  • 透视 HTTP 协议

  • 重学前端

发布于: 2020 年 11 月 16 日阅读数: 60
用户头像

大导演

关注

导演出品,必属精品 2019.01.15 加入

github:https://github.com/directorcn,欢迎 star ⭐

评论

发布
暂无评论
HTTP 前世今生