上次讲到lru与缓存重建,这次主要讲一下关于过期处理的一些主要问题。

在讨论这个问题之前,有个相关的问题须要大家有所了解。

就是对于一个缓存如期仅仅来说,什么东西应该缓存,什么不应该缓存。这是一个比較复杂的问题。涉及到http协议的诸多细节。

这里赵永明大哥写了一篇文章。讲得非常具体,尽管是以ATS为背景讲的,可是思路是想通的,大家能够点击这里去看一下,文章名字非常骚气叫“to
cache or not to cache,一直是个大问题”

在缓存server里。分hit和miss两种行为。前面的文章已经讲过了。server本地有缓存叫hit处理(也会由于if-modified-since转成miss处理。这个后面讲),无缓存是miss处理。过期处理自然发生的本地有对应文件的基础上,miss情况下根本没有校验文件过期与否的动作可言。

这里我以我们cacheserver为例讲一下主要的过期处理流程,当然开源的squid,ats之类的在这块也是大同小异。

一个文件缓存与否。包含缓存多长时间。通常取决于取源返回的响应中关于缓存相关的一些头部,比如:Expires,Cache-Control,Pragma, Last-Modified,Etag, Age。Vary等。这些头的解释。大家能够去翻看协议,不想翻协议的,能够从这里找到一个总结,这里就不浪费口舌了。

可是有一种情况必须考虑,对于CDN来说,他们服务的客户运营水平參差不齐,非常多都没有相关的响应头来告诉CDN厂商,他们想缓存多长时间。通常在这样的情况下,CDN厂商有这样一些规则。要么缓存指定的一段时间,这个时间CDN厂商自己控制。要么针对不同的文件后缀。对缓存时间做更仔细化的控制,这样的情况就不再讨论了。我们接下来重点讨论有正规缓存头部的情况。

当请求到来,cacheserver在本地找到了相应的文件,这里所谓的“找到的文件”多数都是所谓的文件在内存中的索引结构,由于这个结构中通常包含相应磁盘文件的全部信息。

为了方便讨论,我们这里暂且把这个索引结构叫做store。

首先store结构中有一个成员。我们暂且叫cache_time,它记录的是缓存server在最開始存储这个文件时的时间,通俗点说就是取源回来開始缓存这个文件,就把当前时间记录在cache_time这个成员里。也能够说这个时间觉得是缓存開始在本地构建这个文件的时间。

在store结构中通常有一个成员来保存源响应头中Date字段相应的时间戳,我们暂且将这个成员叫做backend_date。这个头通常是后端server在发送响应时拿它的当前时间戳构建的。

为了方便讨论,我们把缓存server的当前时间称为current_time。

  • 首先处理Cache-Control的相关头部。假设cache_time+ max-age < current_time,说明这个文件过期了。这里的max-age是Cache-Control中的一个字段。

    否则当前的验证说明未过期,须要进一步检查其它过期信息。

  • 假设存在源响应带有Expires头,那么比較expires - backend_date与current_time - cache_time的大小。

    首先前者表示这个文件在源server还有多长时间过期,后者表示这个文件在本机创建,当如今已经过去了多长时间。

    假设前者大于后者,意味文件能够继续使用,未过期。当然这是基于第一点中max-age检查。未过期的基础之上的。假设前者小于后者呢?我们就觉得是过期了。可是会出现这样的情况吗?后者的时间差来自于本地server,我们假定是准确的,可是前者的时间来自于其它后端server,不一定准确。就可能出现这样的情况。

    出现了这样的情况,仅仅能当做过期处理。我们也碰到过自己的机器时间不准了导致文件异常过期的bug。

    所以缓存server通常在启动时要确认机器时间是否准确,这点非常重要。

在以上这里点检查通过之后,未标记为过期的,会进到兴许的hit处理流程。过期的就须要取源。

所以取源有两种,第一种是本地没有缓存该文件,还有一种是文件已经缓存过,可是如今过期了。过期回源是,涉及到回源验证的问题。什么是回源验证?当一个文件过期了,你回源又一次取的时候,可能这个文件在源server上并没有改动过,那么这时缓存server再取一遍是非常傻,也是浪费带宽。减少性能的事情。

这个时候,在store结构中有个成员往往保存了一个时间戳,我们暂且叫做mod_time。这个时间戳来自于最開始构建文件时。源给的响应中的Last-Modified头,这个头中的时间告诉了我们。在我们取源获取这个文件时,该文件在源server最后一次改动的时间。

那么我们在回源验证时,将这个mod_time放到我们取源构建的请求头的If-Modified-Since中,源收到这个头之后。就会去查看文件在这个时间以后。有没有被改动过。假设改动过。那么源通过回应304响应。告知下游文件未发送变化,文件能够重用。否则。就通过200响应。发送最新文件。

一般的情况,缓存server都会把响应头跟响应体保存在一个文件里。接下来作为hit处理的兴许流程,往往须要先去读取保存文件里的响应头。当中我们须要关心的是Last-Modified头。由于client可能携带着If-Modified-Since头。我们须要对照两者是否相等,假设相等,那么我们觉得文件在client询问的时候之后没有改变过,我们给他304响应。这里有两个问题:

  1. 为什么不把Last-Modified头放在store结构中,这样我们就不须要去读文件来获取这个头了。

    由于不同于一般的webserver。cache服务器给出的正常响应必须忠于源给出的响应,而非常少有权利自己打包响应头。除非本机发生像5xx等这类情况,所以既然一定要读文件。就没有必要单独拿出来了。其次store结构作为每一个文件的索引。会占用大量的内存。所以降低哪怕一个成员,在大量文件存储时。也能节省不少空间。

  2. 假设client的If-Modified-Since跟我们的Last-Modified不相等,而是大于或者小于,该怎么处理?

    假设前者小于后者,毫无疑问应该回应200,由于我们的最后修改时间比client问的时间要新。

    假设大于呢?我们是否应该去回源验证呢?这样的情况我们直接使用本地文件响应200处理。非常多人觉得这样处理不太合理,毕竟你作为缓存代理。无法知道在“未来”的时间点上。源有没有修改过文件。可是假设你去做回源验证,别人可能因此攻击你。每次都发带有If-Modified-Since将来非常长时间的一个时间点,让你的缓存产生回源,极大的消耗你的性能,由于作为cache,降低回源是最核心的功能。CDN厂商之间的相互攻击早已不是什么新奇事。我们都是交过学费的。

上述提到的这些仅仅是一种最简单的情景。即client -- cache  -- origin。缓存直接跟源站交互。

可是假设cache是分级的,比方client -- cache1 -- cache2 -- origin这样的情况。假设我们cache2对origin给的响应头不做不论什么处理直接转发给cache1。那么对于cache1来说,他收到的结果跟cache2感知的是一样的,那么原来cache2将某文件缓存了多长时间,到什么时间过期这些信息。在cache1这边看来是一样的。这样就会出现故障。比方origin告诉cache2。最大缓存时间max-age为1个小时。那么cache2在缓存了半个小时的时候。收到了cache1的请求,这时cache2将该文件发送给它,那么对于cache1来说,它也会把文件缓存一个小时。

这样一个本应该在CDN中缓存1小时的文件。却被缓存了至少(1小时+半小时)长的时间。

对于使用了CDN的origin来说。无论CDN服务商的cache是几级,架构是什么样。1小时的缓存时长是绝对的,不同意被CDN放大。

这样的情况下CDN必须採取一定的措施来规避这个问题。

普通情况下origin在响应头中携带的Expires。Date这些跟缓存时间相关的头是不同意改动的。除非origin有些个性化的需求。主动告知CDN去做哪些改动。事实上这是主要的规则。特别是CDN在做反向代理的时候,应该对client来说是透明的,不能说请求经过CDN和不经过,origin的某些原始响应头会发生变化。那怎么办?最主要的就是使用Age这个头。只是这个头在CDN里用的比較少。由于这个头是在HTTP1.1里才支持的,对于现阶段网络上大量HTTP1.0的请求来说。这个头就没法用了。所以CDN往往干脆自己加一个私有的头来替代Age的作用。Age事实上就是记录一个文件在一个server上从产生到如今,经过了多长时间,所以字如其名。“年龄”的意思。除了这个头这外还须要另外一个相对的max-age。

什么叫相对的max-age?我们能够觉得origin给出的max-age是CDN系统内存活的绝对时间。而在一个多级cache的缓存系统里,当前cache中文件能存活的最大时间。应该是绝对的max-age时间减掉在上级(多层)cache已经存活过的时间,得到的差值就能够叫做相对max-age。

​一般CDN自定义的头通常都使用X-xxx这种形式。所以这里我们就把相对的max-age时间用X-max-age来表示。Age时间用X-age来表示。origin给max-age用Max-Age。这样每层cache在发送响应头的时候应该携带X-max-age。即X-max-age(要发给下级的)=X-max-age(上级给的) - X-age。在这个公式里,假设上级没有给X-max-age,那么意味这当前cache的上级是origin。那么此时等式变为
X-max-age = Max-Age(origin给的) - X-age。至于X-age的计算就非常easy了。用前面提到current_time - cache_time就可以。

这里须要注意一点。假设cache被杀掉重新启动。那么X-max-age,X-age这些信息应该是延续的,而不能重置。所以这就要求cache的设计者把这些信息持久化,仅仅是简单的留在内存中是不行的。

缓存server设计与实现(五)的更多相关文章

  1. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  2. atitit。浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结

    atitit.浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结 1. 缓存的一些机制 1 1.1. http 304 1 1.2. 浏览器刷新的处理机制 1 1.3. Expir ...

  3. 朱晔的互联网架构实践心得S1E9:架构评审一百问和设计文档五要素

    朱晔的互联网架构实践心得S1E9:架构评审一百问和设计文档五要素 [下载文本PDF进行阅读] 本文我会来说说我认为架构评审中应该看的一些点,以及我写设计文档的一些心得.助你在架构评审中过五关斩六将,助 ...

  4. 简单服务端缓存API设计

    Want 我们希望设计一套缓存API,适应不同的缓存产品,并且基于Spring框架完美集成应用开发. 本文旨在针对缓存产品定义一个轻量级的客户端访问框架,目标支持多种缓存产品,面向接口编程,目前支持简 ...

  5. Eureka Server设计(转载 石杉的架构笔记)

    目录: 一.问题起源 二.Eureka Server设计精妙的注册表存储结构 三.Eureka Server端优秀的多级缓存机制 四.总结 一.问题起源 Spring Cloud架构体系中,Eurek ...

  6. 专訪阿里陶辉:大规模分布式系统、高性能server设计经验分享

    http://www.csdn.net/article/2014-06-27/2820432 摘要:先后就职于在国内知名的互联网公司,眼下在阿里云弹性计算部门做架构设计与核心模块代码的编写,主要负责云 ...

  7. Mybatis源码研究7:缓存的设计和实现

    Mybatis源码研究7:缓存的设计和实现 2014年11月19日 21:02:14 酷酷的糖先森 阅读数:1020   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  8. DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  9. .NET 缓存模块设计

    上一篇谈了我对缓存的概念,框架上的理解和看法,这篇承接上篇讲讲我自己的缓存模块设计实践. 基本的缓存模块设计 最基础的缓存模块一定有一个统一的CacheHelper,如下: public interf ...

随机推荐

  1. iOS-- UIimageView详解

    原文地址: http://blog.csdn.net/djxiaoyu_haha/article/details/40348377 // (1)创建 UIImageView *imageView = ...

  2. 《Linux兵书》

    <Linux兵书> 基本信息 作者: 刘丽霞    杨宇 丛书名: 程序员藏经阁 出版社:电子工业出版社 ISBN:9787121219924 上架时间:2014-1-13 出版日期:20 ...

  3. mysql求交集:UNION ALL合并查询,inner join内连接查询,IN/EXISTS子查询

    两个要求交集的表(列)的结构要一致,对应的字段数,字段类型都应该相同:将两个数据的数据列用 UNION ALL 关键字合并:将上面的所有需要比较的列 GROUP BY :最后 HAVING COUNT ...

  4. IIS - 无后缀(无扩展名)的MIME类型配置

    添加一个新的 MIME 类型即可. 文件扩展名:. MIME类型:application/octet-stream

  5. openssl https 单向认证连接成功示例

    研究这个玩意也有几天的时间了,刚学C 因为不熟悉编译折腾了不少时间,终于弄通了,发个随笔给研究openssl https的同学一点提示吧. 环境: ========================== ...

  6. iOS:网络编程解析协议一:HTTP超文本传输协议

    HTTP传输数据有四种方式:Get方式.Post方式.同步请求方式.异步请求方式.具体的介绍,前面已经有过系统的讲解,这次主要进行具体的举. 说明:同步和异步请求方式在创建链接对象和创建请求对象时,用 ...

  7. SRM 212 Div II Level Two: WinningRecord,Brute Force

    题目来源:http://community.topcoder.com/stat?c=problem_statement&pm=3003&rd=5858 比较简单. 代码如下: #inc ...

  8. 数学图形(1.29) cochleoid曲线

    它也算是一种螺线吧 相关软件参见:数学图形可视化工具,使用自己定义语法的脚本代码生成数学图形.该软件免费开源.QQ交流群: 367752815 #http://www.mathcurve.com/co ...

  9. SPSS Clementine 数据挖掘入门3

    转摘:http://www.cnblogs.com/dekevin/archive/2012/04/27/2473683.html 了解SPSS Clementine的基本应用后,再对比微软的SSAS ...

  10. 模拟jQuery中的ready方法及实现按需加载css,js实例代码

    这篇文章介绍了模拟jQuery中的ready方法及实现按需加载css,js实例代码,有需要的朋友可以参考一下     一.ready函数的实现经常用jQuery类库或其他类库中的ready方法,有时候 ...