在添加 CDN 后客户端访问经常出现访问到历史旧数据的情况,下面我们分析客户端到服务器端整条链路的情况分析出现历史旧数据的原因以及规避方法。
客户端浏览器缓存
现象分析
如解密浏览器缓存机制中所述,浏览器缓存是包括强缓存和验证缓存两种模式。因为强缓存是不会与服务器端进行任何校验直接加载资源,因此客户端访问加载了强缓存的数据会直接导致获取到历史旧数据(如图 1 所示)。
图1. 浏览器强缓存示意图
而如果客户端获取的浏览器验证缓存也同样是有可能获取到历史脏数据的。因为浏览器端会和其访问资源的服务器端进行 Etag 或者 Last-Modified 字段验证,如果其访问的服务器为缓存服务器(例如: CDN 等缓存服务器)。而同样缓存服务器也是历史脏数据的话就会导致异常(如图 2 所示)。
图2. 浏览器验证缓存示意图
规避方案
1. 客户端清空浏览器缓存
当怀疑浏览器强缓存导致的问题可以手动清空下浏览器缓存或者通过 Ctrl+F5 强制刷浏览器缓存查看是否恢复。
2. 禁止浏览器缓存
上述方法仅能够临时解决,如果该资源不想其因为浏览器缓存导致脏数据的话可以设置该资源的 Response 头中的 Cache-Control 或者 Expires 为 no-cache, no-store, private即可禁止浏览器缓存。
注意:上述的 Response 头中的 Cache-Control 或者 Expires 设置也同样会影响 CDN 的缓存。如果希望 CDN 加速的资源可以在 CDN 上缓存但是禁止在浏览器缓存的话可以在 CDN 上设置 HTTP 头,该 HTTP 头不影响 CDN 缓存策略,仅影响客户端浏览器缓存策略(配置如图 3 所示)。
图3. CDN 设置 HTTP 头示意图
劫持访问到缓存服务器
现象分析
终端设备正常请求应该是到缓存服务器 CDN 或者服务器上,但是有时会出现该资源请求被 301 或者 302 跳转到某台服务器返回数据,例如图 4 所示,该 IP 并非是服务器的 IP ,被跳转到运营商的缓存服务器。 这种现象一般出现在局部地区的个别终端设备中,并非在全局范围内影响。
图4. 访问出现劫持示意图
规避方案
1. 反馈当地运营商跟进该问题
因为该问题主要在客户端到服务器端的运营商链路出现的问题,通过服务器端不好解决该问题,建议可将该问题反馈给运营商解决该问题。
CDN 缓存历史脏数据
现象分析
CDN 作为内容分发网络,是会将用户源站的资源缓存到各个 CDN 节点。 CDN 当源站做同名更新时是不会主动回源拉取新资源的( OSS 除外,详细见规避方案 4 )。除非当客户端发起请求后,对应 CDN 节点上该资源没有缓存或者缓存已经过期才会回源拉取最新的数据。
用户可以通过获取资源的 Response 头中查看到该资源的缓存情况。如图 5
中所示, Via 投中分别标识了 CDN 的 L2 节点和 L1 节点,如果两者之中有一个是 "H" 的状态即表示 CDN 是命中状态,此时是不会回源拉取最新数据的。图 5 中即是 CDN L2 节点命中的状态。
图5. CDN 的Response 头示意图
规避方案
1. 手动刷新 CDN 缓存,重新触发回源请求
如果需要将 CDN 上的缓存强制置为过期的话是可以手动刷新 CDN 上的缓存数据的,详细操作请您参考 CDN 刷新缓存,另外如果应用端需要脚本进行控制的话可以使用刷新的 API 接口,请您参考 CDN 刷新缓存接口。
注意: CDN 的目录刷新是会刷新该目录下的所有子目录和文件的,因此目录刷新是有可能引起大量回源请求的。
2. 源站尽量避免同名更新,可以给文件增加版本号区别
建议用户静态资源做版本更新的时候可以通过版本号进行区别,例如在 URL 中带上 “?version=1.0” 进行区分,这样避免应用系统迭代后仍然获取之前版本数据。
注意:通过版本号进行区别时 CDN 是不能开启过滤参数或者保证版本的参数保留的,请您参考: CDN 过滤参数的作用。
3. 资源经常同名更新则应该设置其不在 CDN 缓存
如果对应的资源经常需要更新的话那么该资源应该在 CDN 缓存时间较短或者不缓存的,建议用户可以根据实际的业务场景配置缓存规则, CDN 的缓存规则建议参考: CDN 缓存策略。
4. 当 CDN 的源站为 OSS 时可以设置自动刷新功能
当 CDN 的源站是 OSS 的话是可以设置自动刷新功能的,请您参考图 6 。当 CDN 的加速域名也同样在 OSS 的自定义域名进行绑定后即可开启该自动刷新功能。然后当 OSS 中的文件出现同名更新的话就会自动下发 CDN 刷新任务以保证 CDN 上该资源的缓存不可用。
图6. OSS 配置 CDN 缓存自动刷新示意图
代理服务器缓存
现象分析
常见的用户架构中是 CDN 回源到反向代理服务器,然后通过反向代理到真正的源站服务器。在这种场景下如果反向代理服务器开启了缓存功能也同样会在源站服务器进行更新后仍然出现历史脏数据。测试通过代理服务器和源站服务器返回数据不一致即可确认为该问题。
规避方案
1. 关闭代理服务器的缓存功能
反向代理服务器的缓存功能用户是可以选择是否开启缓存功能。例如使用 Nginx 服务器作为反向代理服务器时是通过 proxy_cache 模块设置缓存的。用户可以关闭该模块功能即可取消缓存的功能,其他的 Web 服务器配置具体见对应服务器的官方配置文档。
2. 清空代理服务器缓存
用户在更新过源站服务器后可以清除代理服务器的缓存内容,不同的代理服务器有不同的清除方法,在 Nginx 服务器中是没有直接提供清除指定 URL 缓存的功能的,常见的方法是使用 ngx_cache_purge 第三方模块进行清理。