谷歌CDN随机未提供gzip内容

10
我希望您能为我提供建议,这是否是Google CDN的错误还是我错过了什么。我发现了这个错误大约4个月前,试图联系他们的支持团队,但他们非常粗鲁,以至于我不想在这里谈论那件事。他们接受了,并告诉我他们将把问题发送给后端团队,但之后他们删除了问题跟踪器,并且不再回复我的电子邮件。这就是我在这里询问的主要原因。
问题:
Google CDN随机地不向最终用户提供gzip内容。因此,他们下载了500KB文件而不是约70KB。我无法直接模拟此问题到我的源,但我可以很容易地在Google CDN上产生此问题。
以下是对CDN的示例请求:
请求:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8,bg;q=0.6,hr;q=0.4,mk;q=0.2,sr;q=0.2
Cache-Control:no-cache
Connection:keep-alive
Cookie: example
Host: example.com
Pragma:no-cache
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36

响应:

Accept-Ranges:bytes
Age:58422
Alt-Svc:clear
Cache-Control:public, max-age=604800
Content-Length:550158
Content-Type:text/css
Date:Tue, 04 Apr 2017 03:45:53 GMT
Expires:Tue, 11 Apr 2017 03:45:53 GMT
Last-Modified:Sun, 19 Mar 2017 01:50:22 GMT
Server:LiteSpeed
Via:1.1 google

正如您所看到的,我的请求具有accept-encoding:gzip头,但我收到的内容并非gzip。我收到的是500KB而不是70KB。还请注意Age头,该项已被缓存/存在CDN上58422秒!

这里是另一台机器(美国)发送的相同请求

请求:

:authority: xxx
:method:GET
:path:/wp-content/themes/365/style.css
:scheme:https
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
accept-encoding:gzip, deflate, sdch, br
accept-language:en-US,en;q=0.8
cache-control:no-cache
cookie: xxx
pragma:no-cache
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

响应:

accept-ranges:bytes
age:58106
alt-svc:clear
cache-control:public, max-age=604800
content-encoding:gzip
content-length:72146
content-type:text/css
date:Tue, 04 Apr 2017 03:49:28 GMT
expires:Tue, 11 Apr 2017 03:49:28 GMT
last-modified:Sun, 19 Mar 2017 01:50:22 GMT
server:LiteSpeed
status:200
vary:Accept-Encoding
via:1.1 google

如您所见,我从我的另一台服务器上获取了一个gzip内容。

我有大量的HAR文件和视频可以证明这个bug,但是让我们保持简单。Google CDN日志可以在GCP仪表板中获得,检查一下它们是什么样子。

enter image description here

如果我的所有访问者都不支持gzip,那么GoogleBot怎么办?

enter image description here

我还分析了我的服务器日志,发现文件的99%响应大小为gzip,只有少数请求不是gzip。这很合理,因为一些访问者或机器人请求该文件时没有gzip头。

暂时解决办法
如果我清除CDN缓存,这个问题在接下来的几分钟/小时内就不存在了。过一段时间后,它仍然会出现。而且这个问题并不总是发生,而是随机发生。我有一个系统来解析CDN日志并显示图表,这实际上就是我发现这个错误的方法。

enter image description here

每当我看到图表带宽增加(是正常情况的两倍),当我登录到Google控制台并检查日志时,我会发现那些500KB的日志文件中有50%的文件请求,而且在浏览器中容易产生错误,我只需登录我的服务器,请求该文件并获取随机结果。
如果问题出在我的源头上,我会很高兴,因为我可以在一分钟内解决,但我认为这是Google CDN的错误。如果有任何更了解CDN技术的人来协助我或来自Google Cloud的人,我将非常高兴。
编辑:
正如我所说,这个问题发生在随机的时间框架内,这是我现在录制的视频,展示了一个“无错误时间框架”。正如您所看到的,每个响应都被压缩。 NO BUG TIME FRAME CDN VIDEO 编辑2:
这是一个图表,显示了单个.css URL测试的gzip和非gzip响应数量。

stacking lines

编辑3:

在第一张图像上,线条是可堆叠的,这里是没有堆叠的相同图像。正如您所看到的,有些小时几乎100%的响应没有使用gzip。

not stacking lines

编辑4:

这是同一个CSS文件的原始解析日志。

1060个请求的响应大小在100KB以下。200、304、206响应代码。 32个请求的响应大小超过100KB。200和206响应代码。

origin server

编辑5:

分析了4月1日至7日的日志,以下是单个.css URL的一些额外统计数据:

19803个CDN请求使用了>100KB(未压缩)

41004个CDN请求使用了<100KB(gzip)

29个>100KB(未压缩)的缓存来自源站点

924个<100KB(gzip)的缓存来自源站点

423个>100KB(未压缩)的缓存来自缓存

2295个<100KB(gzip)的缓存来自缓存

我很惊讶缓存之间的填充非常有效,太神奇了。

解决方案

源站点没有错误,Google CDN也没有错误。问题在于当Google CDN接收到一个可缓存的实体,并且请求没有发送“Accept-Encoding:gzip”的情况下,Google CDN将存储该未压缩响应并覆盖所有已存储的压缩缓存实体。所以下一次用户尝试获取某个文件(例如.css),Google CDN会回答:

  1. 我从源站点接收到此文件,并且它没有通过任何方式更改。
  2. 发送未压缩的响应。
请注意,网络服务器没有配置在没有“Accept-Encoding:gzip”头的请求上发送“Vary: Accept-Encoding”头。我在Litespeed、Apache、Nginx和Cloudflare Nginx上测试过。 我强烈建议Google团队更新相关文档。虽然文档中有关于“Vary headers”的一些说明,但是由于没有人(包括我自己、Google的一级支持、Stack Overflow或其他人)能理解这个问题,因此需要更新说明。
此外,文档还说:
In addition to the request URI, Cloud CDN respects any Vary headers that instances include in responses.

当请求没有'Vary'头时,什么也不会发生。
这是我如何修复它:
<FilesMatch '.(js|css|xml|gz|html|txt|xml|xsd|xsl|svg|svgz)$'>
    Header merge Vary Accept-Encoding
  </FilesMatch>
1个回答

5

Google Cloud CDN不会对来自您原始服务器的响应进行压缩或解压缩。相反,它会尊重源服务器的Vary: Accept-Encoding响应头,并基于客户端的Accept-Encoding请求头缓存不同的变体。支持gzip压缩的客户端应该获得一个变体,而不支持gzip压缩的客户端则应该获得另一个变体。

问题在于,您提供的未压缩示例响应缺少Vary: Accept-Encoding头:

Accept-Ranges:bytes
Age:58422
Alt-Svc:clear
Cache-Control:public, max-age=604800
Content-Length:550158
Content-Type:text/css
Date:Tue, 04 Apr 2017 03:45:53 GMT
Expires:Tue, 11 Apr 2017 03:45:53 GMT
Last-Modified:Sun, 19 Mar 2017 01:50:22 GMT
Server:LiteSpeed
Via:1.1 google

以下响应指示Cloud CDN为所有客户端使用未压缩的变体,而不管他们是否支持gzip压缩。一旦没有Vary:Accept-Encoding标题的响应出现在缓存中,Cloud CDN将为所有客户端使用该缓存响应。解决方法是让源服务器在其响应中包括Vary:Accept-Encoding头。
您能分享一下如何启用gzip压缩的详细信息吗?看起来有时您的源服务器未在其响应中包含Vary:Accept-Encoding头。也许当它认为客户端不支持gzip压缩时,它不会包含该标题?

这只是非常基本的响应,当然我的源包括Vary头,即使假设我的源不包括Vary头,大小也应该相同,因为我还解析了我的服务器日志,99%的响应大小都是“gzipped”。 - Novkovski Stevo Bato
您分享的未压缩响应缺少“Vary: Accept-Encoding”头,这就是问题的原因。只需要一个这样的响应。一旦缓存了该响应,Cloud CDN将为所有客户端使用该缓存响应。您能分享源服务器URL吗?如果可以的话,我可以尝试重现这个问题。 - elving
以下是一个您可以使用的命令以重现问题。只需将example.com替换为正确的主机名,将42替换为唯一的数字即可: curl -s -D - -o /dev/null 'http://example.com/wp-content/themes/365/style.css?42' - elving
我很惊讶Cache-To-Cache是如此有效(请查看EDIT5),因此源中的单个请求错误可能会导致这种统计数据。您建议在我的源日志中包括“Accept-Encoding”请求标头和“Vary”响应标头,以便我们可以看到带有“Accept-Encoding:gzip”标头的请求是否具有与不带gzip标头相同的响应?如果我们在我的源中找到这种日志,那么我认为我们接近解决此问题。另一种方法是将WAF规则放置到返回403/404的位置,如果请求具有gzip标头但响应超过100KB。 - Novkovski Stevo Bato
不需要记录日志;如果您运行我提供的curl命令,您会发现源服务器将返回一个没有Vary: Accept-Encoding响应头的响应。该响应将覆盖任何具有Vary头的现有缓存条目。在从源服务器接收到这样的响应后,Cloud CDN将停止为所有客户端提供压缩变体,并使用未压缩的变体。阻止对带有Accept-Encoding: gzip请求的未压缩响应是不够的。相反,您需要阻止任何没有Vary: Accept-Encoding响应头的响应。 - elving
让我们在聊天中继续这个讨论 - Novkovski Stevo Bato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接