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

缓存概念

缓存处理请求步骤

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

注意:

  • 创建响应时候要注意版本匹配,如果服务器响应和客户端请求的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. 使用阿里云镜像安装tensorflow

    pip --default-timeout=1000 install --index-url https://mirrors.aliyun.com/pypi/simple tensorflow pip ...

  2. 转载C#文件下载的实现

    一.//TransmitFile实现下载     protected void Button1_Click(object sender, EventArgs e)    {        /*     ...

  3. 读书笔记<<世界是部金融史>>

    1.权力只对来源负责.权力只会对其来源负责--孟德斯鸠<论法的精神> 2.能违反的是纪律,不能违反的是规律.人自然要遵从人性规律. 3.在金融市场中有一个法则,如果市场认为一件事是真的,那 ...

  4. Drf_序列化and反序列化基础

    序列化类 from rest_framework import serializers class BookSerializer(serializers.Serializer): # 要序列化的字段 ...

  5. Golang 挑战:编写函数 walk(x interface{}, fn func(string)),参数为结构体 x,并对 x 中的所有字符串字段调用 fn 函数。难度级别:递归。

    golang 挑战:编写函数 walk(x interface{}, fn func(string)),参数为结构体 x,并对 x 中的所有字符串字段调用 fn 函数.难度级别:递归. 为此,我们需要 ...

  6. 解决ubuntu 20.04、22.04 即新版本 fcitx 无法使用的问题

    前提 已在系统设置中将fcitx设置为默认 fcitx开机自启 配置的过程不在本文讨论范围之内 开机自启可通过安装gnome-tweaks配置实现 问题分析流程 手动启动fcitx时提示设置XMODI ...

  7. 游戏AI——GOAP技术要点

    目录 什么是GOAP(Goal-Oriented Action Planning) 介绍 细节 难点与挑战 世界表达 具体类型表示 字符串表示 bool转化为枚举 规划器 Regoap流程 Middl ...

  8. 企业实践 | 国产操作系统之光? 银河麒麟KylinOS-V10(SP3)高级服务器操作系统基础安装篇

    [点击 关注「 全栈工程师修炼指南」公众号 ] 设为「️ 星标」带你从基础入门 到 全栈实践 再到 放弃学习! 涉及 网络安全运维.应用开发.物联网IOT.学习路径 .个人感悟 等知识分享. 希望各位 ...

  9. Low-Code,一定“low”吗?

    作者:京东保险 吴凯 前言 低代码是一组数字技术工具平台,基于图形化拖拽.参数化配置等更为高效的方式,实现快速构建.数据编排.连接生态.中台服务.通过少量代码或不用代码实现数字化转型中的场景应用创新. ...

  10. 阿里版ChatGPT:通义千问pk文心一言

    随着 ChatGPT 热潮卷起来,百度发布了文心一言.Google 发布了 Bard,「阿里云」官方终于也宣布了,旗下的 AI 大模型"通义千问"正式开启测试! 申请地址:http ...