CDN缓存的理解

​CDN​​​即内容分发网络​​Content Delivery Network​​​,​​CDN​​​的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求,​​CDN​​​的基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定,通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层虚拟网络,​​CDN​​​系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上,其目的是使用户可就近取得所需内容,解决​​Internet​​网络拥挤的状况,提高用户访问网站的响应速度。

组成

  • 从功能上看,​​CDN​​系统由分发服务系统、负载均衡系统和运营管理系统组成:分发服务系统主要负责资源的响应、缓存和同步。负载均衡系统主要负责均衡单点多个内容缓存设备的负载,并对内容进行缓存负载平衡及访问控制,以及对用户请求进行调度以及路由。运营管理系统则负责运营需求管理和网络系统管理。
  • 从节点分布上看,​​CDN​​​系统主要分为边缘层和中心层,边缘层分布在​​CDN​​网络的边缘位置,给用户提供就近访问服务,中心层则负责完成资源同步和运营管理等功能。中心层保存了加速域名的相关配置信息比如源站域名,也缓存了加速域名下的各种资源,在边缘层节点未命中缓存时,需要向中心层节点发起请求,而中心层节点未能命中缓存时,需要查找对应的源站域名,并向该源站域名发起请求,然后再逐层返回并缓存用户请求的资源。

功能

  • 节省骨干网带宽,减少带宽需求量。
  • 降低通信风暴的影响,提高网络访问的稳定性。
  • 提供服务器端加速,解决由于用户访问量大造成的服务器过载问题。
  • 能克服网站用户分布不均的问题,并且能降低网站自身建设和维护成本。
  • 提供资源访问缓存,实现相同对象的访问降低响应延迟,并减少主干网带宽占用。

关键技术

  • 缓存算法决定命中率、源服务器压力、​​POP​​节点存储能力。
  • 分发能力取决于​​IDC​​​能力和​​IDC​​策略性分布。
  • 负载均衡决定最佳路由、响应时间、可用性、服务质量。
  • 基于​​DNS​​​的负载均衡以​​CNAME​​实现最优节点服务。
  • 缓存点有客户端浏览器缓存、本地​​DNS​​服务器缓存。
  • 缓存内容有​​DNS​​地址缓存、客户请求内容缓存、动态内容缓存。
  • 支持协议如静动态加速、图片加速、​​HTTPS​​带证书加速、下载加速等等。

配置

使用​​CDN​​​服务提供商的​​CDN​​服务时,需要做一些配置:

  • 解析一个子域名,可以先随意解析到某个地址,例如是​​cdn.example.com​​。
  • 到服务提供商添加该域名,并设置源站域名,例如是​​www.example.com​​。
  • 此时服务商一般会分配一个​​CNAME​​​地址,例如是​​cdn.example.com.service.com​​。
  • 将第一步的域名添加​​CNAME​​​记录为分配的​​CNAME​​地址。
  • 或者服务商在第一步即提供了​​CNAME​​地址,那么直接解析即可。

访问流程

简单的​​CDN​​​的访问流程,这是一种​​pull​​的方式拉取缓存:

  • 访问资源时,从上述的子域名中加载资源文件,​​DNS​​解析该域名。
  • 返回​​CNAME​​​地址,之后解析​​CNAME​​地址。
  • 获得​​CNAME​​​域名对应的​​IP​​​地址,指向​​CDN​​边缘层节点。
  • ​CDN​​边缘层节点未命中资源缓存,则向中心层节点请求。
  • 中心层节点未命中资源缓存,则进行回源,到源站域名服务器获取资源。
  • 成功获取资源后逐层返回并将资源缓存。
  • 在这个查找资源的过程中域名可能会发生变化,但是资源的​​path​​是不会变化的。
  • 之后再进行访问,则直接能够从边缘节点取得缓存而不用回源,加快资源访问速度。

缓存控制

在计算机中有两大难题,一是缓存何时失效,二是如何命名,而​​CDN​​​中缓存何时失效是一个比较麻烦的问题,假如源站的资源文件发生变化,而用户此时取得的资源是从缓存节点中取得的,此时就会造成资源文件不一致的现象,解决这个问题可以通过主动​​push​​​刷新所有​​CDN​​​缓存的方式来实现,但是这种方式成本较高,比较简单的解决方案就是在固定时间段过后便使缓存失效,除了节点的缓存需要控制,还需要控制用户本地缓存,在​​HTTP​​协议中提供了如下缓存控制的方式:

强缓存

强缓存是通过​​Expires​​​与​​Cache-Control​​来控制缓存在本地的有效期。

Expires

​Expires​​​是​​HTTP 1.0​​​提出的一个表示资源过期时间的​​Header​​​,它描述的是一个绝对时间,由服务器返回。​​Expires​​​受限于本地时间,如果修改了本地时间,可能会造成缓存失效.对于资源的请求,如果在​​Expires​​之内,则浏览器会直接读取缓存,不再请求服务器。

Expires: Sun, 14 Jun 2020 02:50:57 GMT

Cache-Control

​Cache-Control​​​出现于​​HTTP 1.1​​​,优先级高于​​Expires​​,表示的是相对时间,请求头和响应头都支持这个属性,通过它提供的不同的值来定义缓存策略。

Cache-Control: max-age=300
  • ​Cache-Control: no-store​​: 缓存中不得存储任何关于客户端请求和服务端响应的内容,每次由客户端发起的请求都会下载完整的响应内容。
  • ​Cache-Control: no-cache​​​: 缓存中会存储服务端响应的内容,只是在与服务端进行新鲜度再验证之前,该缓存不能够提供给浏览器使用。简单来说,就是浏览器会将服务端响应的资源进行缓存,但是在每次请求时,缓存都要向服务端评估缓存响应的有效性,协商缓存是否可用,根据响应是​​304​​​还是​​200​​判断是使用本地缓存资源还是使用服务器响应的资源。
  • ​Cache-Control: public || private​​​:​​public​​​表示该响应可以被任何中间人比如中间代理、​​CDN​​​等缓存。默认响应为​​private​​​,​​private​​表示该响应是专用的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。
  • ​Cache-Control: max-age=31536000​​​: 响应为最大的过期时间,其指令是​​max-age=<seconds>​​​,表示资源能够被缓存即保持新鲜的最大时间,​​max-age​​是距离请求发起的时间的秒数。
  • ​Cache-Control: must-revalidate​​​: 当使用了​​must-revalidate​​​指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。在正常情况下是没有必要使用这个指令的,因为在强缓存过期的情况下会进行协商缓存,但是​​HTTP​​​规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候,还比如有配置一些特殊指令​​stale-while-revalidate​​​、​​stale-if-error​​​等的时候,​​must-revalidate​​指令就是让缓存在过期后的任何情况下都必须重新验证。

协商缓存

当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的​​HTTP​​​状态为​​304 (Not Modified)​​​,该请求不携带实体数据,若未命中,则返回​​200​​​并携带资源实体数据。协商缓存是利用的是​​Last-Modified,If-Modified-Since​​​和​​ETag、If-None-Match​​​这两对​​Header​​来管理的。

Last-Modified If-Modified-Since

​Last-Modified,If-Modified-Since​​​是​​HTTP 1.0​​​引入的,​​Last-Modified​​​表示本地文件最后修改日期,浏览器会在请求头加上​​If-Modified-Since​​​即上次响应的​​Last-Modified​​​的值,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,但是如果在本地打开缓存文件,就会造成​​Last-Modified​​​被修改,所以在​​HTTP 1.1​​​出现了​​ETag​​。

ETag If-None-Match

​Etag​​​就像一个指纹,资源变化都会导致​​ETag​​​变化,跟最后修改时间没有关系,​​ETag​​​可以保证每一个资源是唯一的,​​If-None-Match​​​的请求头字段会将上次返回的​​Etag​​​发送给服务器,询问该资源的​​Etag​​​是否有更新,有变动就会发送新的资源回来。​​ETag​​​的优先级比​​Last-Modified​​​更高,具体使用​​ETag​​主要出于下面几种情况考虑:

  • 一些文件也许会周期性的更改,但是他的内容并不改变,比如仅仅改变的修改时间,这个时候我们并不希望客户端认为这个文件被修改了,而重新​​GET​​。
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,例如​​1s​​​内修改了​​N​​​次,​​If-Modified-Since​​能检查到的粒度是秒级的,这种修改无法判断。
  • 某些服务器不能精确的得到文件的最后修改时间。

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://zhuanlan.zhihu.com/p/40682772
https://baike.baidu.com/item/CDN/420951
https://blog.csdn.net/pedrojuliet/article/details/78394732