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

缓存概念

缓存处理请求步骤

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

注意:

  • 创建响应时候要注意版本匹配,如果服务器响应和客户端请求的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. 初学TCP的一些感想

    因为工作原因,想学习一下编程,最近在学习Qt相关的知识,学到了TCP/IP这里,跟着视频做了一个简单的C/S的小例程.例程中没有关于连接状态的判断.经过修改,可是没能达到我要的效果,在百度上进行搜索, ...

  2. 关于rust cargo下载依赖慢的解决方法(转载)

    网址: https://zhuanlan.zhihu.com/p/74875840?from_voters_page=true win环境基本输入两个命令就好了 $env:http_proxy=&qu ...

  3. Go_day04

    Go基础语法 指针 指针式存储另一个变量内存地址的变量 &a 取出a的内存地址 *b 若指针b存放的式a的地址 那么 *b就直接指向a的内存 可以直接操作其中的值 指针的使用 func mai ...

  4. VSCODE C# 运行 找不到任务"BUILD"----C#常用命令

    使用 Visual Studio Code 创建 .NET 类库 - .NET | Microsoft Docs 安装vscode.vscode c#相关拓展.MINIGW64 1.创建文件夹 2.用 ...

  5. bootstrapv4轮播图去除两侧阴影及线框

    一.前提条件:  在使用bootstrap v4中的轮播图组件时,两侧默认出现阴影,且轮播组件示例不一致! 二.bootstrap文档组件展示与实际应用 1.官方文档展示如下:没有阴影 2.实际应用情 ...

  6. FastReport OpenSource发布到Linux上的准备

    一.安装libgdiplus(libgdiplus是一个Mono库,用于对非Windows操作系统提供GDI+兼容的API)   apt-get install build-essential lib ...

  7. 《MySQL是怎样运行的》第六章小结

  8. SHELL-反弹shell

    什么是shell? 在我们深入了解发送和接收 shell 的复杂性之前,了解 shell 实际上是什么很重要.用最简单的术语来说,shell 就是我们在与命令行环境 (CLI) 交互时使用的工具.换句 ...

  9. Winhex 恢复三个分区

    通过之前的博客WinHex恢复分区,我们知道 在MBR中,一共有四行是我们分区的引导,所以,我们手动只能创建三个主分区,通过工具软件,我们最多可以创建四个主分区 那么测试:如何创建四个主分区? 首先, ...

  10. 如何理解redis两种不同的持久化方式

    其实redis就是一种高级的以键值对形式存储数据的数据库,而它的好处就是他可以支持数据的持久化,其实redis之所以会有这样的优点,主要是因为,redis的数据都是存放在内存中的,如果不配置持久化,那 ...