HTTP 协议(包体的传输方式&缓存的工做原理)

HTTP 协议(包体的传输方式&缓存的工做原理)

这篇文章主要了解一下 HTTP 协议中定长包体传输的格式和不定长包体传输的格式,而后简单介绍一下 HTTP 协议中缓存的工做原理和应用场景。浏览器

1.HTTP 包体

1.1 HTTP 包体格式

请求或者响应均可以携带包体,基于 ABNF 描述的格式以下:缓存

#message-body = *OCTET:二进制字节流
HTTP-message = start-line *(header-field CRLF) CRLF [message-body]

1.2 不能携带包体的请求或响应

  • HEAD 方法请求时对应的响应不能携带包体。
  • 1xx204304 状态码对应的响应。
  • CONNECT 方法对应的 2xx 响应。

1.3 发送定长包体格式

在发送 HTTP 消息时已经可以肯定包体的所有长度,格式以下:服务器

#使用 Content-Length 头部明确指明包体长度
Content-Length = 1*DIGIT
Tips: 1*DIGIT 表示用 1 个 十进制(不是十六进制)数表示包体中的字节个数,必须与实际传输的包体长度一致,它的优势是接收端处理简单。

1.4 发送不定长包体格式

发送 HTTP 消息时不能肯定包体的所有长度,须要使用 Transfer-Encoding 头部指明使用 Chunk 传输方式,含有 Transfer-Encoding 头部后 Content-Length 头部会被忽略。不定长包体优势以下:网络

  • 基于长链接持续推送动态内容。
  • 压缩体积较大的包体时,没必要彻底压缩完(计算出头部)再发送,能够边发送边压缩。
  • 传递必须在包体传输完才能计算出的 Trailer 头部。
transfer-coding = "chunked" / "compress" / "deflate" / "gzip" / transfer-extension

Chunked transfer encoding 分块传输编码:Transfer-Encoding:chunked

chunked-body = *chunk
                last-chunk
                trailer-part
                CRLF
                
chunk = chunk-size[chunk-ext] CRLF chunk-data CRLF

#chunk-size-1*HEXDIG:注意这里是十六进制

chunk-data-1*OCTET

last-chunk = 1*("0")[chunk-ext] CRLF

trailer-part = *(header-field CRLF)

2.经常使用 HTTP 缓存应用场景

以下图所示,以百度首页为例,展现了 HTTP 缓存场景:
curl

Tips: disk cache 表示磁盘缓存,下次访问的时候不须要下载,能够直接去磁盘获取, memory cache 表示缓存存在内存中,当浏览器退出进程时,内存中的数据会被清空。

3.HTTP 缓存实现示意图

Tips:以 HTTP 请求中部分信息(如 schema、path、host)构成的字典, HTTP 响应消息构成的 LRU 链表。

4.判断缓存是否过时

4.1 freshness_lifetime

判断缓存是够过时可使用以下格式的公式计算:ide

#freshness_lifetime:按优先级,取如下响应头部的值

#s-maxage > max-age > Expires > 预估过时时间

# Cache-Control:s-maxage=3600
# Cache-control:max-age=86400
# Expires:Fri,03 May 2019 03:15:20 GMT
# Expires-HTTP-date,指明缓存的绝对过时时间
response_is_fresh = (freshness_lifetime > current_age)

freshness_lifetime 以下图所示:
编码

下面给出各类缓存时间请求统计占比状况:
url

Tips:缓存的预估时间计算参照 RFC7234文档中 (DownloadTime-LastModified)*10%

4.2 current_age

  • Age 头部

Age 头部表示来自原服务器发出响应(或者验证过时缓存),到使用缓存的响应发出时通过的秒数,对于代理服务器管理的共享缓存,客户端能够根据 Age 头部判断缓存时间,格式以下:spa

Age = delta-seconds
  • current_age 计算公式以下:
:current_age = corrected_initial_age + resident_time

4.3 模拟浏览器缓存工做原理

步骤1:从浏览器复制一个缓存相关的请求命令:
以下图所示,能够在百度首页显示有缓存的资源右键复制出相关的 HTTP 请求的 curl 命令:

命令以下:代理

curl 'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/min_notice-816c20c940.js' -H 'Referer: https://www.baidu.com/'  -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36' --compressed -I
Tips:命令最后增长一个 -I 表示返回输出头部内容。

请求结果以下图:

步骤2:使用 If-None-Match 条件判断请求内容是否过时:

curl 'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/min_notice-816c20c940.js' -H 'Referer: https://www.baidu.com/'  -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36' --compressed  -H 'If-None-Match' -I

以下图所示:

Tips: HTTP 请求时带上 If-None-Match 能够判断 Etag 指纹对应缓存内的容是否过时,若返回 304 表示缓存能够继续使用。

5.私有缓存和共享缓存

  • 私有缓存:仅供一个用户使用的缓存,一般只存在于浏览器这样的客户端。
  • 共享缓存:能够供多个用户的缓存,存在于网络中负责转发消息的代理服务器(对热点资源常使用共享缓存,以减轻原服务器的压力,并提高网络效率)
  • Authentication 响应不可被代理服务器缓存

6. Cache-Control 头部

Cache-ControlABNF 中的秒数以下:

Cache-Control = 1#cache-directive 

cache-directive = token ["=" (token / quoted-string)]

delta-seconds = 1*DIGIT
Tips: RFC规范中的要求是至少能支持到 2147483648(2^31)

6.1 请求中头部的 Cache-Control

请求中 Cache-Control 的取值有 max-agemax-stalemin-freshno-cacheno-storeno-tansformonly-if-cached,它们的含义以下:

  • max-age:告诉服务器,客户端不会接受 Age 超出 max-age 秒的缓存。
  • max-stale:告诉服务器,即便缓存再也不新鲜,但陈旧描述没有超出 max-stale 时,客户端仍打算使用,若 max-stale 后没有值,则表示不管过时多久客户端均可使用。
  • min-fresh:告诉服务器,Age至少通过 min-fresh 秒后缓存才可使用。
  • no-cache:告诉服务器,不能直接使用已有缓存做为响应返回,除非带着缓存条件到上游服务端获得 304 验证返回码才可以使用现有缓存。
  • no-store:告诉个代理服务器不要对请求的响应缓存(实际有很多不遵循该规定的代理服务器)。
  • no-tansform:告诉代理服务器不要修改消息包体的内容。
  • noly-if-cached:告诉服务器仅能返回缓存的响应,不然若没有缓存则返回 504 错误码。

6.2 响应中头部的 Cache-Control

响应中 Cache-Control 的取值有 max-ages-maxagemust-revalidateproxy-revalidateno-cacheno-storeno-transformpublicprivate,它们的含义以下:

  • must-revalidate:告诉客户端一旦缓存过时,必须向服务器验证后才可使用。
  • proxy-revalidate:与 must-revalidate 相似,但它仅对代理服务器的共享缓存有效。
  • no-cache:告诉客户端不能直接使用缓存的响应,使用前必须在源服务器验证获得 304 返回码。若是 no-cache 后指定头部,则客户端的后续请求及响应中不含有这些头则可直接使用缓存。
  • max-age:告诉客户端缓存 Age 超出 max-age 秒后则缓存过时。
  • s-maxage:与 max-age 类似,但仅针对共享缓存,且优先级高于 max-ageExpires
  • public:表示不管私有缓存或者共享缓存,皆可将该响应缓存。
  • private:表示该响应不能被代理服务器做为共享缓存使用,若 private 后指定头部,则在告诉代理服务器不能缓存指定的头部,但可缓存其余部分。
  • no-store:告诉全部下游节点不能对响应进行缓存。
  • no-transform:告诉代理服务器不能修改消息包体的内容。

7.什么样的 HTTP 响应会被缓存

  • 请求方法能够被缓存理解(不只仅 GET 方法)
  • 响应码能够被缓存理解(40四、206也能够被缓存)
  • 响应与请求的头部没有指明 no-store
  • 响应中至少应含有如下头部中的 1 个或者多个:
Expires、max-age、s-maxage、public
#当响应中没有明确指示过时时间的头部时,若是响应码很是明确,也能够缓存
  • 若是缓存在代理服务器上
不含有 private
不含有 Authorization

8.使用缓存做为当前请求响应的条件

  • URI 做为主要的缓存关键字,当一个 URI 同时对应多份缓存时,选择日期最近的缓存。例如 Nginx 中默认的缓存关键字:proxy_cache_key
$scheme$proxy_host$request_uri;
  • 缓存中的响应容许当前请求的方法使用缓存
  • 缓存中的响应 Vary 头部指定的头部必须与请求中的头部相匹配:
Vary = "*" / 1#field-name

vary:*意味着必定匹配失败
  • 当前请求以及缓存中的响应都不包含 no-cache 头部(Pragma:no-cache或者Cache-Control:no-cache)
  • 缓存中的响应必须是如下三者之一:
#新鲜的
#缓存中的响应头部明确告知可使用过时的响应(如 Cache-Control:max-stale=60)
#使用条件请求去服务器端验证请求是否过时,获得 304 响应

扫码关注爱因诗贤
在这里插入图片描述

相关文章
相关标签/搜索