CDN 回源之罪魁祸首-etag

      最近网站做推广,预期访问量将会增加20倍左右(原来PV在10亿左右),其中大部分是静态页面,也有ajax动态请求。我们采取了如下策略。

  • 机器扩容到原来的2倍
  • 所有静态内容都加上CDN缓存
  • 所有的动态请求都利用缓存定时更新机制,实现动态内容静态化。
    在灰度放量初期,我们观察JS回源的请求,发现CDN并没有如期挡住请求,单台机器每分钟去到1万左右。找运维人员反复沟通,都说CDN没有问题。运维,开发的一场撕逼大战即将展开。

       经过各种抓包分析排查,发现前端人员利用了Jquery的JSONP功能,会在每个动态请求发出前在的URL后面加时间戳,后面找运维人员沟通得知,CDN厂商在设置缓存的时候对于Key有两种策略,一种是按照文件名,将问号后面的参数去掉。另外一种是将所有的参数作为缓存Key的一部分。 然而,CDN默认为第二种。由于这个动态时间戳在业务上来说其实是没有用的,因此,果断让前端人员去掉。

      然而,事情解决有这么顺利吗? 答案是,No。 经过几天的观察发现回源情况有所缓解,但是回源的请求依然很高。运维组给出的答案是我们的缓存时间设置过短(30s),好吧,我们不得已答应将缓存过期时间调整为1分钟,可是情况依然不乐观,直到后面的有了新发现。

     在我们继续观察中发现一个奇怪的问题,那就是F5刷新每次不到几秒就会返回http 200,而理论上来说我们设置的缓存是1分钟,只要在1分钟的周期内,应该所有的内容都要返回304才对。运维同学们也反复观察发现一个问题,CDN如果回源更新内容的时候,我们会在http header 上 默认带上etag, etag是http1.1的内容,可以比expired time更精准的把控内容的变化,只要内容有任何更新,etag都会不同。可以查看[RFC](http://tools.ietf.org/html/rfc7232#section-2.3)的详细介绍。

An entity-tag can be more reliable for validation than a modification
   date in situations where it is inconvenient to store modification
   dates, where the one-second resolution of HTTP date values is not
   sufficient, or where modification dates are not consistently
   maintained.

后面运维人员发现CDN是这样工作的。

1.根据请求过来的Key去查找缓存,没有则回源

2.如果有则判断浏览器传过来的etag,与缓存里面的etag作比对,如果发现不同就去回源更新。如果相同就返回状态码304来减少网络传输。

       问题就在第2点,只要CDN回源更新了就会导致浏览器的etag与CDN持有的etag不同,而CDN发现etag不匹配就去回源,而回源的结果就是etag又一次的更新,而其他用户的浏览器持有的etag又不同,周而复始,导致回源率特别高。

      其实说到这里才来到了正题,按照etag的规范,服务器判断etag是否同客户端传来的etag匹配,如果不匹配则返回的内容,负责返回304告诉浏览器原来的内容依然有效。实际上与etag类似的header还有if-modified-since, 但是按照协议规范etag会优先启用。但是在这里CDN发现不匹配原本应该认为自己的版本是最新的,但是CDN居然认为自己的内容不新了需要去回源了,个人认为这是CDN需要改进的地方。 既然etag导致了这个问题,去掉了也不会影响,因为还有if-modified-since,所以再一次然运维将整个站点的etag头都去掉了。 过几分钟观察,回源情一直下降,最后下降到几十个。

     我这里标题是 CDN 回源之罪魁祸首-etag, 实际上能不能怪etag呢? 虽然有文章对这个header作批判的,但是我不认为是etag的问题,而是CDN值得改进。这里记录下这个过程是想让看到的同学们避免踏入这个坑。

相关推荐