背景
海外用户访问国内的图片时,需要回源站拉图。由于海外许多国家网络基础设施较差,网速不行,图片回源时间可能会很长,导致很多图片慢加载。
这里有两个优化点:
- 对图片进行压缩,减少拉图片的流量。
- 对压缩后的图片进行预热,让图片提前到达对应国家的 CDN 结点上,缩短图片与用户的物理距离。
阿里云CDN拓扑图
目前使用了阿里云的 CDN 服务,其结点分布拓扑图如下所示。
这里有几个点:
- 阿里云的 CDN 分了层级,对用户外分为 L1 结点、L2 结点和源站。源站是提供资源的服务器,可以是自己的 Nginx 服务器,也可以指定为 OSS 的桶,这样就能直接去阿里云的 OSS 拉取图片。L2 是中间的结点,全球范围内,小国家可能一个国家仅一个 L2 结点,大国家可能有几个。L1 是用户能够直接访问的 CDN 结点,一个国家可能会存在多个,一个 L2 结点会映射到多个 L1 结点。
- 用户通过 CDN 域名请求资源时,该 CDN 域名会被 DNS 服务器解析到最佳的 L1 结点,如果 L1 上已经缓存了资源则直接返回,否则会查询 L2,如果 L2 也没资源则回源查询资源。如果是 L1 回源查询数据,那拿到源站资源后只会缓存到 L1 结点,L2 结点依然没有数据。
CDN资源预热
阿里云的 CDN 提供资源刷新与预热功能。
通过刷新功能,您可以删除 CDN 节点上已经缓存的资源,并强制 CDN 节点回源站获取最新资源,适用于源站资源更新和发布、违规资源清理、域名配置变更等;通过预热功能,您可以在业务高峰前预先将热门资源缓存到 CDN 节点,降低源站压力提升用户体验。
参考文档:help.aliyun.com/document_de…
注意:预热资源是预热到 L2 CDN 结点,此时 L1 即可去 L2 拉资源而无需回源。
阿里云控制台可以实现小规模资源的刷新和预热,对于有大量资源需要预热或者刷新的场景,阿里云提供了相关接口进行自动化操作。参考文档:next.api.aliyun.com/document/Cd…
预热需要鉴权,不要在代码里面明文配 AccessKey 相关的信息。
预热接口需要指定预热的 URL,每次预热会生成一个 Task,预热结果也可以在 阿里云 控制台查看。
指定CDN结点获取资源
由于人在国内,有需求是想测试一下看看国外的 CDN 结点回国内源站拉取资源的表现,这里介绍下方法。
首先,通过下面的网站(查询全球的公共 DNS 服务器)找一个 DNS 的服务器,看看自己的 CDN 域名会被解析到哪里。
随便选一个澳大利亚的:
用 nslookup 请求这个 DNS 服务器,看看自己的域名会解析到哪个服务器。
# cm-res.cretacontent.com 是自己的CDN域名
# 116.251.31.224 是DNS服务器的IP
$ nslookup cm-res.cretacontent.com 116.251.31.224
Server: 116.251.31.224
Address: 116.251.31.224#53
Non-authoritative answer:
cm-res.cretacontent.com canonical name = cm-res.cretacontent.com.cdn.dnsv1.com.
cm-res.cretacontent.com.cdn.dnsv1.com canonical name = 0it6b9ua.ovslegodl.sched.ovscdns.com.
Name: 0it6b9ua.ovslegodl.sched.ovscdns.com
Address: 101.33.26.242
Name: 0it6b9ua.ovslegodl.sched.ovscdns.com
Address: 43.132.81.29
Name: 0it6b9ua.ovslegodl.sched.ovscdns.com
Address: 43.132.80.22
Name: 0it6b9ua.ovslegodl.sched.ovscdns.com
Address: 43.132.80.30
可以看到给出了一系列可能的 CDN 结点地址,在自己的 /etc/hosts 文件加一下解析规则,让到 "cm-res.cretacontent.com" 域名的请求全部解析到指定的 IP:
43.132.81.29 cm-res.cretacontent.com
这样即可实现向指定 CDN 服务器发起请求,这样即使是本地请求,也会去上述 CDN 服务器(L1 结点)发起请求,如果该 L1 结点上没有资源,则由该 L1 结点回源请求资源。
CDN结点资源请求实验
如何能够确定一个资源已经预热到了 CDN 结点上?答案是通过 HTTP 的响应码进行判断。
具体方式为看 via header. 举个例子:
via: cache6.l2vn1[0,0,200-0,H], cache1.l2vn1[0,0], cache5.vn17[0,0,200-0,H], cache3.vn17[1,0]
这里 via 会给出 L1 和 L2 结点的资源缓存命中情况。cache6.l2vn1 表示 L2 结点,cache5.vn17 表示 L1 结点。对应的中括号里面,H 表示 命中,M 表示未命中。
基于上述方式,在本地浏览器请求一下国外(配置了越南的 CDN 服务器)的 L1 CDN 结点,看看响应情况。
(1)不预热图片实验
不进行图片预热。图片ID:18552bbcb55c642
请求一个越南的L1结点(IP: 128.1.60.231)
首次请求(请求时间:16:45分):
ali-swift-global-savetime: 1672130751
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:45:51 GMT
via: cache1.l2vn1[248,247,200-0,M], cache37.l2vn1[249,0], cache7.vn17[250,250,200-0,M], cache1.vn17[251,0]
x-cache: MISS TCP_MISS dirn:-2:-2
x-swift-cachetime: 93312000
x-swift-savetime: Tue, 27 Dec 2022 08:45:51 GMT
再次请求(请求时间:16:47分):
ali-swift-global-savetime: 1672130751
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:45:51 GMT
via: cache1.l2vn1[248,247,200-0,M], cache37.l2vn1[249,0], cache7.vn17[0,0,200-0,H], cache1.vn17[0,0]
x-cache: HIT TCP_MEM_HIT dirn:13:793742575
x-swift-cachetime: 93312000
x-swift-savetime: Tue, 27 Dec 2022 08:45:51 GMT
实验结论:
- 首次请求 L1 结点,L1 未命中资源缓存,此时会回源拉取图片,然后缓存到 L1 上,但是没有缓存到 L2 上(这里不排除阿里云可能还做了次数的统计,也许请求的次数多了就会缓存到 L2 了)!
- 再次请求 L1 结点,资源成功缓存到 L1 上。
(2)图片预热实验
直接调用阿里云的预热接口对图片进行预热. 图片ID:185526c5bf5fcd3
预热时间:2022-12-27 16:01:13:105
预热后等待一段时间后,再通过 CDN 结点获取图片(根据测试结果,一般预热后20秒内可以实现全球预热)。
请求越南胡志明的L1结点(IP: 128.1.60.231)
首次请求(请求时间:16:17分):
ali-swift-global-savetime: 1672128077
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:01:17 GMT # 这里date是预热时间
via: cache6.l2vn1[0,0,200-0,H], cache1.l2vn1[0,0], cache5.vn17[2,1,200-0,M], cache3.vn17[3,0]
x-cache: MISS TCP_MISS dirn:-2:-2
x-swift-cachetime: 93311037
x-swift-savetime: Tue, 27 Dec 2022 08:17:20 GMT
再次请求(请求时间:16:19分):
ali-swift-global-savetime: 1672128077
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:01:17 GMT
via: cache6.l2vn1[0,0,200-0,H], cache1.l2vn1[0,0], cache5.vn17[0,0,200-0,H], cache3.vn17[1,0]
x-cache: HIT TCP_MEM_HIT dirn:12:391635488
x-swift-cachetime: 93311037
x-swift-savetime: Tue, 27 Dec 2022 08:17:20 GMT
结果分析:
- date 表示了资源预热到 L2 的时间。
- L2 结点缓存命中(H),预热成功;L1 结点未命中(M)。说明首次访问这个 L1 结点时没有资源,这时到 L2 结点获取资源,且命中了 L2 中的资源,之后再将资源缓存到 L1 上。
- 再次请求时,L1 也已经缓存了这个资源,此时 L1 也是缓存命中(H).
请求另一个越南胡志明的L1结点(IP: 107.155.62.89)
首次请求(请求时间:16:22分):
ali-swift-global-savetime: 1672128077
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:01:17 GMT
via: cache6.l2vn1[0,0,200-0,H], cache29.l2vn1[1,0], cache14.vn15[2,2,200-0,M], cache10.vn15[3,0]
x-cache: MISS TCP_MISS dirn:-2:-2
x-swift-cachetime: 93310746
x-swift-savetime: Tue, 27 Dec 2022 08:22:11 GMT
再次请求(请求时间:16:23分):
ali-swift-global-savetime: 1672128077
date: Tue, 27 Dec 2022 08:01:17 GMT
via: cache6.l2vn1[0,0,200-0,H], cache29.l2vn1[1,0], cache14.vn15[0,-1,200-0,H], cache10.vn15[2,0]
x-cache: HIT TCP_MEM_HIT dirn:13:418829285
x-swift-cachetime: 93310746
x-swift-savetime: Tue, 27 Dec 2022 08:22:11 GMT
结果分析:
- 表现与前一个胡志明的 L1 结点一致,资源被成功预热到越南的 L2 结点。
再请求一个加拿大的L1结点(IP: 47.246.24.215)
首次请求(请求时间:16:35分):
ali-swift-global-savetime: 1672128079
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:01:19 GMT # 预热时间 加拿大预热完成时间晚2秒
via: cache22.l2us1[0,0,200-0,H], cache7.l2us1[0,0], ens-cache10.us18[2478,2477,200-0,M], ens-cache16.us18[2481,0]
x-cache: MISS TCP_MISS dirn:-2:-2
x-swift-cachetime: 93309944
x-swift-savetime: Tue, 27 Dec 2022 08:35:35 GMT
再次请求(请求时间:16:36分):
ali-swift-global-savetime: 1672128079
cache-control: max-age=31536000
date: Tue, 27 Dec 2022 08:01:19 GMT
via: cache22.l2us1[0,0,200-0,H], cache7.l2us1[0,0], ens-cache10.us18[0,0,200-0,H], ens-cache16.us18[1,0]
x-cache: HIT TCP_MEM_HIT dirn:12:103600147
x-swift-cachetime: 93309944
x-swift-savetime: Tue, 27 Dec 2022 08:35:35 GMT
实验结论:
- 加拿大物理距离更远,资源预热成功时间比越南稍晚。
- 资源成功预热到加拿大的 L2 结点,能够实现全球预热。
(3)URL带参数预热实验
测试预热一个带自定义参数的图片,图片 ID:185588f9885b673
预热URL(这里参数是随意定的):cm-res.cretacontent.com/api/apolo-i…
这里就不贴测试结果了。
结论:CDN 预热是以 URL 为资源的 Key 进行缓存的,只要 URL 有任何改变,都会导致 CDN 进行回源请求。因此图片压缩预热时,一定要预设好图片的宽高和质量等参数,后面不要调整,否则所有请求都会穿透 CDN 回源站。