闲话 CDN

3,716 阅读10分钟
原文链接: zhuanlan.zhihu.com

开头

这篇文章通过 FCC 上海线下和成都微信的分享,整理成文字稿顺便凑一下更新,考虑到吃瓜读者们不知道都了解到啥程度,以及我科普作者的身份(自己定的),我决定从入门到放弃的介绍一下,大致涉及:

  • 什么是 CDN
  • 为什么我们要用 CDN
  • 访问原理
  • 架构
  • 应用与踩坑
  • 现实世界的 CDN

由于每个地方都事无巨细讲起来非常费劲,费劲就容易跳票,而且会导致篇幅过长,所以其实都是科普向的,如果想要深入,在每个地方都会给出链接,可以进行针对性的深入阅读。

如果有说的不对的地方请各位大佬指摘,粗体部分为超链接。

什么是 CDN

从一个简单的栗子说起:

「非洲农业不发达,人人都要金坷垃」——相信大家基本上都看过来自美国圣地亚哥的视频,美国人、非洲人和日本人在一起抢来抢去。如果金坷垃只在一个地方生产,那么非洲的运输成本和生产者的产能压力都很大。

那么很简单,我们在世界的每个需要金坷垃的国家都开代工厂,都生产金坷垃——我们的 CDN 就是生产金坷垃的公司,而一个个「节点」就是代工厂。

CDN,中文名叫做「内容分发网络」,它的作用是减少传播时延,找最近的节点,实际上,尽管互联网帮助我们实现了地球村,但是从中国到日本和从中国到台湾的时延仍旧是不一样的,这一点可以从 pingtraceroute 中看出。

CDN 的优点

访问加速

CDN 作为前端性能经典手段,相信大家已经无脑使用了,正如前面所说的,减少了时延,从很大程度上就能作为加速手段了。实际上,真正的 CDN 并不是前面举例的一个国家一个节点,甚至是一个运营商,一个省份乃至地区都会有节点。

减轻源站(服务器)负载

一个非常简单就能想明白的问题,如果 CDN 已经能帮我返回数据了,那么请求就不会到达源站,源站(服务器)的负载就减轻了。

抗住攻击

既然源站的负载被减轻了,那么在受到 DDOS 攻击的时候,也能谈笑风生。

当年阮老师被 DDOS 闹的满城风雨,后来阮老师就把内容开始迁移到 GayHub……

然后本来我不用更新内容,就在最近,阮老师发布了一篇 DDOS 防御指南,然后接着被攻击,又瘫痪了,防御指南中说自己受到了 CC,然后迁移到了腾讯云,啪啪啪打了我的脸……当然,其实 CC 并没有那么难防御,但是不在分享主题内容中,感兴趣的可以之后聊……

受到阮老师启发,于是我糊了一个架构图,一个博客系统的思路图,基本上和市面上的 Jekyll 和 Hexo 一样,当时我的设想是,把评论之类的全部抽出来,这样打的时候最多打挂评论之类的,对于博文本身不会有任何影响。后来我回去查了一下,发现这不就和市面上的一样嘛——不错!

那么既然静态资源能上 CDN,我们的 API,我们的 MVC 页面能不能也上 CDN?答案是可以的,关键就是这个叫全站加速的东西。

啥玩样儿?其实 CDN 就是一个缓存,区别只是这个缓存是放在网络服务提供商节点的。

最简单的模型像这张图,这张图来自《大型网站技术架构 核心原理与案例分析》一书。

访问原理

从我们发起请求,到到达 CDN 节点,到底经过了哪些东西,CDN 是怎么加速我们的请求的呢?

这张图也是网上找来的。

首先我们在地址栏键入一个网址,浏览器发现本地没有关于这个网址的 DNS 缓存,所以向网站的 DNS 服务器发起请求。

网站的 DNS 服务器设置了 CNAME,指向了某个 CDN 服务器,也就是我们常见的阿里云、腾讯云、Cloudflare 之类的,去请求 CDN 中的智能 DNS 均衡负载系统。

均衡负载系统解析域名,把对用户响应最快的节点返回给用户,然后用户向该节点发出请求。

如果是第一次访问该内容,CDN 服务器会向源站请求数据并缓存,否则的话,直接在缓存节点中找到该数据,将请求结果发给用户。

对于最简单的 CDN 系统而言,只要一台 DNS 调度服务器和一个节点服务器即可,但在复杂的应用中,会存在多级缓存,多台 Cache 来协同工作。

这里我之前在博客里写过类似的内容(实际上就是摘抄的)

架设原理

如果你要架设一个 CDN,大概需要怎么做?记住我们刚才介绍的内容,重点是,CDN 仍然是一个缓存。

拿来阿里云 CDN 的架构图口胡一下,我也没有搭建过,如果解释的不对请大家指出。

既然是缓存,那么很明显,也就是均衡负载加上缓存调度的搭配,根据我们刚才所说的访问原理,其实主要的重点除了均衡负载与缓存外,就是一个中央的 DNS 调度器。

实际上,和计算机的多级缓存设计以及后端的多级缓存设计一样,每一层的 cache 一级比一级大,可以存储更多资源,但是响应一个比一个耗时,如果在 L1 中无法命中,那么我们就会去 L2 找,L2 无法命中才会回到源站,这样可以有效的避免回到源站过于频繁的问题。

接下来这张图,我就有点编不下去了,在阿里,主要使用 LVS + Tengine 做负载均衡,然后用 Swift 做 HTTP 缓存。这是他们自己说的,但是和我没什么关系,我主要讲的是左边那个花括号的一致性 Hash。

一致性 Hash 就是把对象映射在 232 个桶空间里,像一个闭合的圆环,当然,实际上,我们将机器也映射到这个圆环中,比如利用别名或者 IP,顺时针将对象将内容存储到离自己最近的机器中,删除和添加节点也一样,添加和删除节点之后,根据顺时针迁移,原来的对象会进行重新计算。

关于这点需要详细了解可以看这篇:blog.csdn.net/cywosp/arti…

然后说完这个关键性算法,我们就差不多消化了——隔壁阿里云 CDN 是怎么搞出来的。

当然这依旧和我们没啥关系……

应用与踩坑

最常见的应用,就是用于前端静态资源的加速,实际上,利用 CDN,我们甚至可以做出一套属于自己的 jsDelivr

不过,使用 CDN 的时候,有一些基本法需要我们了解。

缓存设置

第一,缓存的设置,max-age 我们都用过,在 Cache-Control 中经常用于缓存的控制,可是 max-age 设置的缓存会应用于一个请求经过的每一级,如果我们希望 CDN 不缓存那么久,要怎么办呢?那就是 s-maxage,它用于设置代理服务器的缓存时间,会覆盖 max-age 的设置,这样我们可以把 max-age 用于本地缓存,把 s-maxage 用于 CDN 缓存时间,避免脏数据的产生。

缓存命中率

对于一个缓存而言,还有一点很重要,就是你的缓存到底有没有用,衡量这个东西的就是缓存命中率。如果只是静态资源,在刷新缓存之后,可能会导致命中率下降,因此 CDN 的资源不适合经常刷新,换句话说,如果一个请求结果会经常进行变更,那么 CDN 基本就没什么存在的意义。

判断是否命中缓存

无论是我们自己在开发过程中,还是帮客户 debug 的过程中,我们都会考虑一件事——这个资源是否命中了CDN,是否是因为CDN导致的问题,这个时候就要秀一波操作了。

在各大厂商的 CDN 返回的数据中都会有一个 X-Cache,上面内容是 Hit 或者 Miss,还会加上诸如 Memory 或者 Disk 的缩写表示内存还是磁盘,如果出现 Upstream 或者 Miss 则表示没有命中。

资源预热

缓存设计中,预热是很重要的环节,在最初刚开始启动 CDN 的时候,CDN 上并没有缓存数据,此刻大量的请求全部打向源站,肯定会把源站打挂,预热就是实现缓存好热门数据,这样在业务上线时,CDN 上已经有所需的数据了。

Vary

此外很多 CDN 都不支持 Vary 头,这样 CORS 需要的 Vary: Origin 就没法保证了,遇到这种情况,比如你发现 Origin 头被缓存了,就只能把跨域头改为 * 去匹配。

Range

另外如果是很大的文件,往往是用 RANGE 头分片载入的,但如果 CDN 没有进行分片,就会重复向源站请求完整资源,CDN 就白搞了,启用 RANGE 回源,就可以减少流量的损耗,正确的设置 RANGE 回源,就能够正确的命中 CDN 缓存。

无私钥 HTTPS

另外重点说一下:这年头上 HTTPS 已经是常规基本法了,而不上 HTTPS 才是个喷点,CDN 为了避免篡改和劫持,当然也得上 HTTPS,但这样就导致我们必须要将证书和私钥传输到 CDN 的平台中去,对于安全性是一个隐患。

所以就有了无私钥解决方案,用户搭建私钥服务器,由 CDN 方去请求签名。图为阿里云的实现,只要在自己的服务器上部署 KeyServer 和配置就可以用了。

这里我在上海 FCC 分享的时候有同学想要具体了解一下,具体来说的话涉及到很多 HTTPS 加密的事情,暂时不做展开,可以看一下 CloudFlare 发的一篇文章,我正好找到了翻译版:www.zcfy.cc/article/key…

现实世界的 CDN

比如,节点挂了,直接导致的是用户的损失,尤其是体量大的公司依赖 CDN 进行静态资源管理的时候,发生这样的事情后果会非常严重。

其次,便宜没好货:本来在只有网宿而没有阿里云的时代,CDN 是很昂贵的,阿里腾讯在拉低 CDN 价格的同时,也拉低了 CDN 的质量,部分节点的访问质量不太高会导致有些用户访问的网络质量非常差。

然后,一个微小的科普:什么是混合 CDN——混合 CDN 这个名词看着很高端,实际上就是,我们用了多家厂商的 CDN,可能也包括自己建的,然后谁好的选谁,但是有的时候反而会造成服务不可控,进一步劣化 CDN 的质量。

总结

CDN 这个东西本质就是一个缓存,只是这个缓存离你特别特别的近,作为用户还是开发都能从中享受到一点福利,但作为一个服务于企业的开发人员,不仅要考虑 CDN 的优点,也要知道 CDN 给我们带来的坑,这样才能靠谱的作为 CDN 的使用者。