声明:本人原创文章,详细内容已发布在我的微信个人技术公众号---网络技术修炼,公众号总结普及网络基础知识,包括基础原理、网络方案、开发经验和问题定位案例等,欢迎关注。

缓存概念

缓存处理请求步骤

缓存如果查询到某个请求已经有缓存,那么需要进一步检查该资源的新鲜度,根据新鲜度和请求中的字段综合评估是否要去服务端拉取新鲜的资源。

注意:

  • 创建响应时候要注意版本匹配,如果服务器响应和客户端请求的http版本不一致,要在缓存服务器做转换。
  • 缓存有通用的日志规范,常见的日志为Squid日志格式和网景的可扩展通用日志。
  • 缓存中过期的资源也不一定要删除,因为过期了也能用,删除一般采用特定算法,如LRU。

以http get为例缓存处理逻辑如下图所示。

服务端响应写入缓存

相关http字段

  • 缓存服务器如果不支持Range和Content-Range,那么不要缓存不完整的response。
  • 缓存服务器在未接收完一个Content-Range的content时候,不要给客户端该部分的应答。
  • 缓存服务器可以将多个Content-Range的content片段组合到一起应答给客户端。

缓存响应的创建

Vary

Vary 是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该使用一个缓存作为响应还是向源服务器请求一个新的响应。当响应中有vary字段的时候,缓存时候必须将vary字段一并缓存,下次请求的时候除了url外,还需要与vary缓存的字段完全一致才可以返回缓存内容。

例如,如果响应的 Vary 字段设置为 "Accept-Language",那么在缓存响应时,代理服务器和客户端应该按照请求中的 Accept-Language 字段来区分不同的响应。这意味着,如果一个客户端发出了两个不同的请求,其中一个请求的 Accept-Language 字段是 "en-US",另一个请求的 Accept-Language 字段是 "fr-FR",那么这两个请求应该获得不同的响应。

如果 Vary 字段为空,那么代理服务器和客户端可以缓存响应,并在任何请求中重用它。

内容协商除了vary,主要靠Accept来实现,Accept 字段,详见下表:

请求头字段

说明

响应头字段

Accept

告知服务器发送何种媒体类型

Content-Type

Accept-Language

告知服务器发送何种语言

Content-Language

Accept-Charset

告知服务器发送何种字符集

Content-Type

Accept-Encoding

告知服务器采用何种压缩方式

Content-Encoding

新鲜度

相关http字段

判断一个缓冲是否过期可以使用以下公式:

response_is_fresh=freshness_lifetime > current_age

其中freshness_lifetime为新鲜度声明周期,current_age为缓存已经生存的时间,这两个值都是使用的相对时间,后面有详细的计算方法。

注意:这个计算只能说明缓存中的内容是否新鲜,具体能否直接将该内容回复给客户端,还需要结合请求头中cache-control的max-age、max-stale、min-fresh字段进行综合判定。这个也比较好理解比如客户购买了一个苹果,吃的时候会看一下是否在保质期内,刚刚就是计算是否在保质期内,但不同人的处理可能不同,有的人发现过期两天也会吃,有的人发现临近过期就不吃了,请求中cache-control的max-age、max-stale、min-fresh字段就代表了不同处理方式的人。综上,关于缓存服务器对于一个存在的缓存资源是否能直接返回给客户端应该先计算freshness_lifetime - current_age,然后再综合请求中cache-control判断。

freshness_lifetime的计算

新鲜度生命时间(freshness_lifetime)表示资源从诞生到过期的相对时间(以秒为单位),其计算按照如下优先级依次计算,如果某个优先级的字段符合计算条件则跳过后面的计算:

推荐使用Date和Last-Modified 计算的缓存间隔时间除以10,即:

if ((last_modified > 0) && (date > 0) && (date - last_modified) > 0) {
return (date - last_modified) / 10;
}

current_age的计算

current_age表示资源从诞生到现在的相对时间( 以秒为单位),其参与current_age的计算的因素有:

  • Age字段表示实体从产生到现在以秒为单位经过多长时间了(多级缓存的场景,上一级缓存会填充该字段)。
  • Date字段表示报文内容在源服务器中诞生的时间。
  • 主机时间。

计算方法:

方法1: 用收到响应的时间减去Date字段的值。

用response_time表示收到响应的时间,用date_value表示Date字段的值,那么

current_age = response_time - date_value

但是,接收端和源服务器间很可能会有clock skew(时钟偏差),为了防止这种情况,将负数结果赋值为0,所以该计算方案最终为:

current_age = max(0, response_time - date_value)

方法2:逐跳计算。

接收端收到响应报文时的Age值等于上一跳节点中缓存的Age值加上传输时延。用previous_hop_age_value表示上一跳节点中缓存对的Age值,用response_delay表示传输时延,那么计算公式如下:

current_age = previous_hop_age_value + response_delay

respose_delay可以粗略地计算为得到响应时间减去发出请求的时间,这里你可能会问,为什么不要再除以二呢,因为HTTP对Age的计算策略是宁可多算也不肯少算的,多算顶多缓存新鲜时间变短,产生额外的新鲜度验证,但是少算的话,即使过期了,客户端还会把它当成新鲜的用。

response_delay = response_time - request_time

这种方法的好处是response_time和request_time都是本地的时间,不存在时间偏差。

综上所述,通常响应报文的计算会综合上述两种方法,取最大的一个。

#第一种计算方式
age_value_by_date = max(0, response_time - date_value)
#第二种计算方式
response_delay = response_time - request_time
age_value_by_hop = previous_hop_age_value + response_delay
#两种取大的
current_age = max(age_value_by_date, age_value_by_hop)

校验

相关http头

  • 条件验证通过服务器回复304 Not Modified,再验证实效返回200 ok。
  • 条件验证标签如果同时存在为“与”的逻辑关系,都满足才能返回304 Not Modified。

缓存控制

Cache-Control为http1.1上定义的缓存控制策略。

TODO:请求中优先级是什么,比如no-cache和only-if-cached如果同时存在,需要去服务端条件get吗?

其他

Pragma

Pragma: no-cache 含义与Cache-Control:no-cache相同,是为了兼容http 1.0版本的字段,当报文头同时出现Cache-Control和Pragma时候,Pragma被忽略。

Cache Control Extensions

详见:https://httpwg.org/specs/rfc7234.html#cache.control.extensions

Warning

此信息通常用于警告应用于消息有效负载的缓存操作或转换可能引入的错误。详细解释见:https://httpwg.org/specs/rfc7234.html#header.warning

Age

current_age的计算章节已经提到Age字段表示实体从产生到现在以秒为单位经过多长时间了源站服务器不会添加字段,但是缓存服务器会添加,也为多级缓存计算current_age提供了一个重要参数。详见:https://httpwg.org/specs/rfc7234.html#header.age

Via

Via 是一个通用首部,是由代理服务器添加的,适用于正向和反向代理,在请求和响应首部中均可出现。

参考文档

Hypertext Transfer Protocol (HTTP/1.1): Caching https://httpwg.org/specs/rfc7234.html

Hypertext Transfer Protocol -- HTTP/1.1 https://www.rfc-editor.org/rfc/rfc2616#section-14.44

Hypertext Transfer Protocol (HTTP/1.1): Range Requests https://www.rfc-editor.org/rfc/rfc7233

《http权威指南》

HTTP中缓存的使用期计算(Age Calculation)https://blog.csdn.net/sxh951026/article/details/77934463

rfc7234之http缓存的更多相关文章

  1. http2协议翻译(转)

    超文本传输协议版本 2 IETF HTTP2草案(draft-ietf-httpbis-http2-13) 摘要 本规范描述了一种优化的超文本传输协议(HTTP).HTTP/2通过引进报头字段压缩以及 ...

  2. 用node探究http缓存

    用node搞web服务和直接用tomcat.Apache做服务器不太一样, 很多工作都需要自己做.缓存策略也要自己选择,虽然有像koa-static,express.static这些东西可以用来管理静 ...

  3. 用ASP.NET Core 2.1 建立规范的 REST API -- 缓存和并发

    本文所需的一些预备知识可以看这里: http://www.cnblogs.com/cgzl/p/9010978.html 和 http://www.cnblogs.com/cgzl/p/9019314 ...

  4. Web开发须知的浏览器内幕 缓存与存储篇(2)

    本文禁止转载,由UC浏览器内部出品. 3. HTTP Cache 综述 HTTP Cache是完全按照IETF规范实现的,最新的RFC规范地址是 https://tools.ietf.org/html ...

  5. 【转】用ASP.NET Core 2.1 建立规范的 REST API -- 缓存和并发

    原文链接:https://www.cnblogs.com/cgzl/p/9165388.html 本文所需的一些预备知识可以看这里: http://www.cnblogs.com/cgzl/p/901 ...

  6. REST API -- 缓存和并发

    REST API -- 缓存和并发 https://www.cnblogs.com/cgzl/p/9165388.html 本文所需的一些预备知识可以看这里: http://www.cnblogs.c ...

  7. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

  8. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  9. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  10. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

随机推荐

  1. beamforming源码标记

    p:各阵元的声压信号矩阵 R:接收数据的自协方差矩阵 Pcbf:交叉谱矩阵

  2. Mysql 行号+分组行号+取Top记录 SQL

    Mysql 行号+分组行号+取Top记录 SQL select * from ( SELECT (@rowNum := @rowNum + 1) as rowNum -- 全量行号 , a.col1 ...

  3. Markdown操作方法

    Markdown学习 标题 三级标题 四级标题 字体 原本 hello,world! 斜体 hello,world! 加粗 hello,world! 斜体加粗 hello,world! 删除 hell ...

  4. SpringBoot解决跨域方案

    SpringBoot解决跨域的几种方式 跨域资源共享(CORS):通过修改Http协议header的方式,实现跨域.说的简单点就是,通过设置HTTP的响应头信息,告知浏览器哪些情况在不符合同源策略的条 ...

  5. [极客大挑战 2019]Upload 1

    进入可以看到,这时一道文件上传题目 直接上传test.php 提示Not image不是图片,可能是MIME绕过,尝试抓包修改Content-Type 提示Not php,猜测可能是检测后缀名不能是p ...

  6. Web For Pentester - SQL injections/Directory traversal

    SQL injections Example 1 典型的SQL注入 name=root' or 1=1 %23 直接执行就可以导出所有用户 查看后端的源码 观察到,我们传入入的name中,传入就成为了 ...

  7. GO语言学习笔记-测试篇 Study for Go ! Chapter ten- Test

    持续更新 Go 语言学习进度中 ...... GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs ...

  8. RTE2021 回顾丨实践中的摸爬滚打,AI OPS 落地之路

    本文整理自声网Agora SD - RTN 网络传输质量负责人于涛在 RTE2021 实时互联网大会上的演讲分享.他在演讲中针对传统 OPS 痛点.AI OPS 的优势以及 AI OPS 工程化的难点 ...

  9. 博客与AI

    最近AI自动生成技术文章和答案在圈子里面引起了很大轰动,Stack Overflow暂时拒绝接收GPT生成的结果.我也经常性地浏览或者编写博客,但是最近我使用new bing或者ChatGPT的过程中 ...

  10. JS中我们为什么要new个实例而不直接执行

    正常情况:<script> function sayHello() { alert("hello") } sayHello();直接调用他 </script> ...