CDN即内容分发网络Content Delivery Network,CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求,CDN的基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定,通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上,其目的是使用户可就近取得所需内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。
使用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是HTTP 1.0提出的一个表示资源过期时间的Header,它描述的是一个绝对时间,由服务器返回。Expires受限于本地时间,如果修改了本地时间,可能会造成缓存失效.对于资源的请求,如果在Expires之内,则浏览器会直接读取缓存,不再请求服务器。
Expires: Sun, 14 Jun 2020 02:50:57 GMTCache-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是HTTP 1.0引入的,Last-Modified表示本地文件最后修改日期,浏览器会在请求头加上If-Modified-Since即上次响应的Last-Modified的值,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,但是如果在本地打开缓存文件,就会造成Last-Modified被修改,所以在HTTP 1.1出现了ETag。
Etag就像一个指纹,资源变化都会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的,If-None-Match的请求头字段会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来。ETag的优先级比Last-Modified更高,具体使用ETag主要出于下面几种情况考虑:
一些文件也许会周期性的更改,但是他的内容并不改变,比如仅仅改变的修改时间,这个时候我们并不希望客户端认为这个文件被修改了,而重新GET。某些文件修改非常频繁,比如在秒以下的时间内进行修改,例如1s内修改了N次,If-Modified-Since能检查到的粒度是秒级的,这种修改无法判断。某些服务器不能精确的得到文件的最后修改时间。