浅谈 DNS 解析与 CDN 原理
在互联网冲浪中,我们已经习惯了输入域名连接网站。也许你有了解过网络世界大家的「坐标」都是 IP 地址,但是 DNS 解析怎么找到 IP 地址?更有时,同一个域名申请还会返回不同的地址,这所谓 CDN 是如何实现的?
DNS 解析流程
把域名转换成 IP 地址,就是 DNS 服务器天天干的事情。DNS 全称为 Domain Name System(域名系统),当我们输入一个域名(如 chralpha.com
),DNS 查询流程大概是:
- 浏览器先查询浏览器缓存,发现并没有找到
chralpha.com
的解析记录 - 浏览器向系统发起请求查询解析结果,通常系统也有一层缓存,但仍然没有找到
chralpha.com
的地址 - 系统向上游 DNS 服务器发起请求,系统中可以自行配置 DNS 服务器,假设我使用的是
1.0.0.1
,那么操作系统就会向1.0.0.1
发送查询请求 - 不论是再向上级查询还是缓存生效,
1.0.0.1
最终都会返回一个对应的 IP 地址,之后浏览器就可以拿着这个 IP 地址发起请求了
至于 1.0.0.1
如何找到 chralpha.com
的 IP 地址呢?如果有缓存,那么直接返回即可。否则将一层层向上查询,直到根域名服务器。全球有 13 台根域名服务器,同时还有几百台备用服务器。根域名服务器会先根据最后的顶级域名,所有域名都必须以顶级域名结尾(如 com
, org
等)。在对应顶级域名位置查询二级域名,也就是 chralpha
。在你注册域名的时候,域名注册商就会给你开通一个地址,里面存放了域名的 NS(Name Server) 信息,用于了解该去哪里查询域名的 DNS 记录。我将域名的 NS 记录修改到 Cloudflare,自然该处保存着 Cloudflare 的地址。此时向该地址请求便能很快找到对应解析记录。
我们刚刚默认系统使用 1.0.0.1
作为 DNS 服务器,事实上这是根据系统设置决定的。你可以自由选择公网或者运营商提供的 DNS 服务器。
![](https://npm.elemecdn.com/hlg@0.0.0/20200411134652.png)
以上就是一次 DNS 解析的完整流程。看似很长,但实则由于缓存机制的存在,通常这个过程是比较无感的,悄无声息间便完成了。
当然,如果你向 chralpha.com
发起请求,得到的并非 200
正常返回值,而是 301
跳转。因为我将 chralpha.com
重定向至 blog.ichr.me
,之后浏览器又会向 blog.ichr.me
发起查询。
CDN 加速原理
从上面的流程来看,一个域名通常对应着一个记录。但是想必你也听说过 CDN 加速。
CDN (Content Delivery Network),内容分发网络。在现有的网络中再添加一层缓存层,并将网站内容分发到多个边缘节点上。受到请求只从距离用户最近的节点调取。
有点类似「空间换时间」的做法。以此可以加快访问速度,降低骨干网压力。
但是同时,我们发现即便是同一个域名,不同位置的用户查询需要返回不同位置对应最近的 CDN 节点。所以在 DNS 解析过程中需要一定特殊手段实现。
访问流程
我们假设 chralpha.com
应用了 CDN 服务,这时如果在浏览器中敲入此域名,和上面 DNS 解析流程一样,浏览器会得到一个记录。但注意这里不再是源的记录,而是 CDN 服务器的记录。通常通过 CNAME 指向 CDN 专用的 DNS 服务器。这个专用 DNS 服务器不只是在缓存索引或者向上级查询这么简单,它会利用请求的 meta 信息,根据发起请求 IP 端判断哪一个边缘节点路由更近,便返回该节点 IP 地址。
![图片来自 [Cloudflare](https://www.cloudflare.com/zh-cn/learning/cdn/what-is-a-cdn/)](https://www.cloudflare.com/img/learning/cdn/what-is-a-cdn/what-is-a-cdn.png)
图片来自 Cloudflare
这只是一个 function 的事情,DNS 查询带来的性能损耗几乎可以忽略不计,但是带来的速度提升却是实实在在的。而且带来的好处还有隐藏源站地址,避免被攻击。
function search(request) {
if (请求对象为 "chralpha.com") {
if (请求发起地址在中国) {
返回位于中国的边缘服务器地址
} else if (请求发起地在北美) {
返回位于北美的边缘服务器地址
}
...
}
}
这段伪代码实现非常粗糙,但原理大致如此。通常 CDN 专用 DNS 服务器可以配置某些高精度 IP 地址库达到更精确的判断效果。
分发方式
一般 CDN 有两种分发手段:Push CDN 和 Pull CDN。
Push CDN 是一种主动分发技术,通常由网站管理员主导。由源节点主动通过 HTTP/FTP 等协议主动分发到边缘节点缓存,如在每次更新的时候将内容预分发到 CDN 节点上。这样无论何时用户向 CDN 节点发起请求时总有最新的缓存可供使用,打开速度也会更快一点。但缺点是不能更好地按需配置,分发所有内容通常会带上一些调用不频繁的资源,降低存储效率,成本也更高。现在 Push CDN 一般可以根据用户访问统计调整分发策略,决定分发那些内容。不过这样一来未分发的资源调用速度会有所下降,各有取舍吧。
Pull CDN 则更像被动分发技术,使用体验视用户访问情况而定。Pull CDN 默认情况下先什么都不动,受到请求时匹配缓存,如果本地有缓存且还没有过期,便直接返回缓存;否则就向源站请求,并将从源站得到的资源缓存下来,设置缓存有效期。Pull CDN 通常配置简单,而且可以根据访问情况灵活调配。但是由于是根据请求缓存,第一次访问或者缓存过期后向源站发起请求速度还是会下降,对于一些流量不大的站点效果远不如 Push CDN。同时还有一定的滞后性,在网站内容更改过后 CDN 节点缓存仍未过期,导致边缘服务器直接返回缓存从而无法及时得到最新内容。这就需要平衡缓存过期时间:过长导致内容时效性影响,过短导致速度体验下降。
由分发方式可知,CDN 需要更多的服务器资源,所以市面上的私人对象存储服务绝大多数都是收费的。有的是按流量计费,有的需要购买流量包等。不过付出一定金额带来的体验上升还是比较可观的,且自己可以更好控制内容分发效果。
与此同时也有许多公共免费 CDN 服务,如 jsDelivr, UNPKG 等等。它们通过调取公共库(如 GitHub、npm 等)的资源并缓存到自己的 CDN 节点上。但是由于「公共」的特殊性,这类 CDN 通常只能使用 Pull CDN。
例如通过 jsDelivr 调用 GitHub 仓库资源,这次 jsDelivr 的 CDN 域名(cdn.jsdelivr.net)会帮我利用专用 DNS 服务器返回距离我最近的 CDN 节点。这时向该节点发出请求。当该节点发现本地无法匹配资源,便会向 GitHub 源位置发起请求,并将得到的资源本地缓存好,设置过期时限。如果有和我挨得比较近的小伙伴也访问这个仓库,那么就会直接从该 CDN 节点返回资源,不用重新回到 GitHub 索取,从而加快访问速度。
但是从 jsDelivr 每日流量来看,除非热门项目,其他资源的命中率是比较低的。而使用 jsDelivr 更多是为了稳定而已。因为 jsDelivr 有网宿备案和国内节点,相比直接访问国内时常抽风的 GitHub,通过 jsDelivr 请求 GitHub 资源或许会更加稳定。
后
说回来,DNS 得以让我们无需再记住毫无规律却随时可能变更的 IP 地址,而 CDN 提升我们上网体验且降低骨干网压力。希望你看完本文后可以了解 DNS/CDN 概念,或对日后应用有所帮助。感谢阅读!
参考链接:
- Sukka - 浅谈在代理环境中的 DNS 解析行为 :详细介绍了不同环境(代理)中的 DNS 解析流程及细节实现差异
- 探索子 - CDN部署过程全记录:原理和实践 :针对 CDN 原理有所涉猎,并详细介绍关于 CloudCone CDN 的配置流程
- https://en.wikipedia.org/wiki/Content_delivery_network
- https://www.cloudflare.com/zh-cn/learning/cdn/what-is-a-cdn/
评论