1.为什么使用CDN?
首先,cdn可以理解为一个普通缓存,如代理缓存或者说边缘缓存,即便不关心用户的具体地理位置,也应该考虑使用cdn的代理缓存来提高用户体验。
简单而言,代理缓存会缓存你网站的一些页面,通过缓存来传输静态内容非常的快。
一个简单的例子:
假设你有一个带有开始页面的博客,这里面列出了所有近期的博客列表。完成这一过程,PHP 脚本要从数据库中获取到最近的文章实体,并且将它们转换成 HTML 结果页并返回给用户。
因此,对于一次请求或者访问包含了:一次php执行+一组数据库的查询,对于1000次请求挥着访问就包含了:1000次php执行+1000组数据库的查询,每一次php执行都要进行cpu,内存和
I/O操作,对于数据库也是一样的。
当CPU 和内存访问到达极限的时候,访问就会出现问题,速度会开始变得非常的慢,甚至出现不能访问的情况,即便可以利用硬件来突破这一瓶颈,但是其工程会变得非常复杂,所以这时就会需要在中间加一层代理缓存,来减少资源对你的限制。
拿前面的例子来讲,使用代理缓存只有第一次请求需要执行 PHP 脚本、查询数据库和生成 HTML 结果页。所有后面过来的请求都会从这个缓存中取内容,读取缓存几乎和直接读取内存一样快。这意味着,上面的线性规模瓶颈的问题解决了!100 个用户或者1000 个用户都没关系,依然只有 1 次 PHP 执行、1 次数据库查询和 1 次的结果页生成。
2.CDN如何起作用?
实际上CDN的类型也有所不同,起分为‘pull cdn’和‘push cdn’两种,顾名思义,‘push cdn’表示你要给cdn提供相应的内容,但是‘pull cdn’则表示如何从cdn中取内容。
* pull cdn如何起作用的呢?*
我们来做个例子,假设你有一个可访问的网站,URL 是 https://www.fooer.com。在这样的场景下,域名fooer.com 会被放到 pull CDN 服务器中,而不是你的网站服务器中。CDN 作为你网站服务器的一个代理。
还有一个不被公开的域名指向实际的网站服务器。在这个例子中假设它是 direct.fooer.com,实际网站服务器叫做源。
这个 CDN 将会接受所有的请求。如果它的缓存中有结果的话将会直接返回给用户,否则会将这个请求托管给你实际的网站服务器,然后把返回的结果缓存起来为以后的请求做储备,同时将结果返回给用户。
最简单的pull cdn的运行过程如下:
获取一个页面的请求,例如这个页面就是fooer.com/some/page
把some/page当作缓存key检查缓存中是否存在
在缓存中则直接从缓存中直接返回结果给用户
不在缓存中则请求https://www.fooer.com/some/page,把返回的结果以/some/page作为key写入缓存,并返回给用户
3.缓存头
绝大多数的 pull CDN 采用以“每页”缓存形式解决动态内容的问题。为了达到这样的效果,一个简单的方法是 HTPP 响应缓存头。
关于http缓存头,新版最常见的就是 ETag 和 Cache-Control 这两个标签,但是旧版还支持Expires,Pragma 和 Age等多个便签,不过这些都是作为向后兼容来使用的。
ETag:
ETag:它是文档版本的标识符。通常是内容的 MD5 值,不过它也可以包含其他内容,代表的是文档的版本/日期,如: 1.0 或者 2017-10-30。这里注意一点是,它必须用双引号括起来,如:ETag: “d3b0756geyg42sd3edec49eaa6238ad5ff00”。
二次验证:
ETag 的实际应用:二次验证。我们暂时不考虑前面代理+源的架构模式,只考虑简单的客户端-服务器模式。
假设客户端请求了 http://www.fooer.com/hello.txt,接着服务端返回如下的内容:
在响应里面,有两个有意思的头标识:一个是 ETag,内容的 MD5值,一个是 Last-Modified,这是 hello.txt 文件最后一次被修改的时间。
这里就是二次验证起作用的地方:当客户端在很短的时间内再次访问上面的 URL,客户端浏览器会使用 If-* 请求头。如 If-None-Match 检查 ETag 的内容是否有改变。也就是说,如果 ETag 发生变化,客户端接收到的一个完整的新响应;如果 ETag 没变化,客户端接收到的是一个表明内容没变化的标识。
如果 ETag 没有改变,那么服务端将会返回:
正如上面所展示的,这次服务器的响应里面不是 200 ok,而是304 Not Modified,这就是说它略过包体部分,让客户端直接去自己的缓存里拿数据。在这个例子中,包体内容是 the body,比较小,效果不明显。可是想象一下如果是很大的内容呢,或者是很复杂的动态生成内容呢,价值就很大了。
我们都知道,你最想缓存起来的是内容,生成内容的代价是最大的,所以 ETag 头是更好的选择。
Cache-Control头
Cache-Control 既可以用于请求头,也可以用于响应头,其次它控制着两个缓存:本地缓存(私有缓存)和共享缓存。
本地缓存:是指在客户端本地机器中的缓存。它并不完全受你的控制,通常浏览器会自己决定是否把某些内容放到缓存中,这意味着:不要依赖于本地缓存。用户也可能在关闭浏览器的时候清理所有缓存,而你并不知道有这样的操作。
共享缓存(CDN):处于客户端和服务器之间的缓存,即CDN。
Cache-Control 有几种属性:
private:表示它只应该存在本地缓存
public:表示它既可以存在共享缓存,也可以被存在本地缓存
no-cache:表示不论是本地缓存还是共享缓存,在使用它以前必须用缓存里的值来重新验证
no-store:表示不允许被缓存。
max-age=:设置缓存时间,设置单位为秒。本地缓存和共享缓存都可以
s-maxage=:覆盖 max-age 属性。只在共享缓存中起作用。
immutable:表示文档是不能更改的。
must-revalidate:表示客户端(浏览器)必须检查代理服务器上是否存在,即使它已经本地缓存了也要检查。
proxy-revalidata:表示共享缓存(CDN)必须要检查源是否存在,即使已经有缓存。
示例如下:
Cache-Control: public max-age=3600 //本地缓存和 CDN 缓存均缓存 1 小时;
Cache-Control: private immutable
//不能缓存在 CDN,只能缓存在本地。并且一旦被缓存了,则不能被更新;
Cache-Control: no-cache //不能缓存。如果一定要缓存的话,确保对其进行了二次验证;
Cache-Control: public max-age=3600 s-maxage=7200
//本地缓存 1 小时,CDN 上缓存 2 小时;
Cache-Control: public max-age=3600 proxy-revalidate
//本地和 CDN 均缓存 1 小时。但是如果 CDN 收到请求,则尽管已经缓存了 1 小时,还是要检查源中文档是否已经被改变。