如果一个网站的图片、JavaScript、CSS、字体、视频片段和软件下载包越来越多,直接让用户访问对象存储虽然能够工作,但当访问量上升以后,真正需要解决的问题就不再只是“文件放在哪里”,而是如何让用户尽可能从距离自己更近的边缘节点拿到资源,同时减少源站请求、降低延迟和控制带宽成本。
在Google Cloud环境中,一个非常典型的架构就是Google Cloud Storage,也就是GCS,负责保存原始静态文件,Cloud CDN负责缓存和边缘分发,而Google Cloud的外部应用负载均衡器负责把这些组件连接起来。用户访问网站时,请求首先进入Google的负载均衡体系。如果对应资源已经存在于CDN边缘缓存中,直接从缓存返回;只有缓存未命中或者资源需要重新验证时,才继续向后端GCS取数据。
这个架构最重要的思想是:GCS负责“存”,CDN负责“发”。不要让对象存储承担所有最终用户的重复下载请求。
例如一个网站每天有几百万次访问同一张logo.png。如果没有缓存,每一次请求最终都可能需要访问源站;如果Cloud CDN已经缓存了这个文件,大量用户可以直接从边缘缓存取得内容。对于CSS、JavaScript、字体和商品图片这类被大量重复请求的资源,这种差异尤其明显。
实际部署时,可以创建GCS bucket保存静态资源,然后使用外部应用负载均衡器的backend bucket把Cloud Storage作为后端,并在该后端启用Cloud CDN。这样用户访问的可以是自己的HTTPS域名,而不是把GCS bucket地址直接暴露给用户。
域名、HTTPS证书和缓存服务因此可以形成一条完整链路:DNS指向负载均衡器,负载均衡器处理HTTPS,Cloud CDN负责缓存,backend bucket把缓存未命中的请求送到GCS。
真正决定CDN效果的,往往不是“开没开CDN”,而是缓存策略。
对于静态资源,最简单也最有效的办法之一是文件版本化。比如原来的app.js不要一直使用同一个URL,而是在内容变化后生成app.20260829.js,或者使用带版本号的资源路径。这样新文件和旧文件实际上是两个不同的缓存对象,CDN不需要等待旧缓存自然过期,浏览器也不会继续使用旧版本。
这也是为什么现代网站经常看到类似app.8f31c.js、style.v42.css这样的文件名。它们并不是为了让程序员炫耀自己的命名技巧,而是为了让缓存系统可以大胆缓存。
对于这种不可变资源,可以设置较长的Cache-Control max-age,甚至配合immutable使用。图片、字体、带hash的JavaScript和CSS尤其适合这种策略。相反,如果一个URL对应的内容随时可能改变,就不应该简单地设置一个很长的TTL然后祈祷用户耐心等待。
缓存策略的基本原则可以概括成一句话:内容不变,缓存时间可以长;URL不变但内容经常变化,缓存时间就必须谨慎。
这里还要注意一个常见误区:CDN缓存键并不是简单地“按目录缓存”。/images/*可以作为资源路径分类,但真正决定不同请求是否共享缓存,还涉及主机名、路径、查询参数以及HTTP请求头等因素。配置缓存键时应该尽量减少没有必要的变化因素,否则本来完全相同的资源可能被拆成大量缓存对象,缓存命中率反而下降。
例如一个图片URL后面无意义地带着不同tracking参数,就可能造成大量缓存碎片。如果业务确实需要查询参数决定内容,就应该明确把相关参数纳入缓存逻辑;如果参数只是统计用途,则应该考虑是否有必要让它影响缓存对象。
压缩也需要正确理解。对于JavaScript、CSS、JSON、SVG等文本资源,gzip或Brotli通常能够显著减少传输量,但图片、视频、ZIP等已经高度压缩的数据通常没有必要再压缩。更重要的是,要正确配置响应头,让浏览器和CDN知道资源的内容编码方式。
大文件则是另外一个问题。
如果网站需要分发数GB甚至更大的安装包、视频或数据文件,瓶颈可能从请求次数变成持续吞吐量、并发下载、源站带宽和客户端网络质量。GCS本身适合保存这类对象,而CDN可以减少重复回源。但不要把GCS的multipart或并行上传机制误认为是“线上CDN加速”。分片上传主要解决对象写入过程中的吞吐和可靠性问题,与用户下载时的边缘缓存是两回事。
安全设计也应该从一开始就考虑。
如果资源完全公开,例如网站logo、CSS和公共JavaScript,可以允许CDN公开缓存。但如果文件属于付费内容、用户私有文件或者临时下载资源,就不能简单地把bucket设置成公开访问。
这种场景可以使用经过认证的访问机制,例如Signed URL或Signed Cookie,让用户在限定时间内获得访问权限。同时应该避免把真正的GCS对象地址作为主要公共入口,否则既不利于统一域名管理,也可能绕过原本设计好的缓存和安全控制。
另外,GCS自身的权限模型也不应该继续依赖传统的逐对象ACL思维。现代Google Cloud项目更常见的做法是使用IAM和统一的bucket级访问策略来控制权限,尽量让“谁能够访问这个bucket”与“用户如何通过CDN获得资源”成为两个清晰的问题。
HTTPS则应该贯穿整个用户访问过程。自己的域名、负载均衡器和Google管理的TLS证书可以组成标准HTTPS入口。对于公共静态网站,用户通常不需要直接看到GCS的内部存储结构。
真正复杂的是多区域和容灾。
很多人会自然地设想:“我在美国和欧洲各放一个GCS bucket,然后CDN哪里近就访问哪里。”实际上没有这么简单。Cloud Storage的bucket位置类型、数据复制方式、负载均衡后端以及CDN缓存机制是不同层面的设计。CDN缓存的是对象副本,并不等于帮你自动建立了一个完整的多区域源站容灾系统。
如果业务对可用性要求很高,需要首先决定GCS采用什么位置架构以及数据如何保持冗余,再设计负载均衡和故障切换。对于某些场景,一个合适的双区域或多区域Cloud Storage架构本身就可以提供比手工维护多个独立bucket更简单的方案。
故障切换还需要考虑缓存状态。某个边缘节点即使已经缓存了热门文件,也不意味着所有资源都已经缓存。如果源站发生故障,大量缓存未命中的请求仍然可能失败。因此,高可用设计不能只测试“CDN缓存命中时还能不能打开网页”,而应该测试冷缓存情况下源站故障会发生什么。
成本同样不能放到最后才考虑。
GCS存储费用、操作费用、CDN缓存和出站流量费用、负载均衡费用以及日志费用都可能随着访问量增长。一个静态网站最容易出现的错误,就是为了追求理论上的高性能,把所有日志、所有缓存策略和所有跨区域架构全部打开,最后发现网站速度快了几毫秒,账单却快了很多。
因此,部署完成以后应该持续观察CDN cache hit ratio、回源请求量、响应延迟、HTTP状态码、带宽以及GCS请求量。如果CDN命中率长期很低,就应该追查资源是否真的适合缓存、Cache-Control是否正确、URL是否被大量无意义查询参数拆散,以及请求是否存在Cookie或其他条件导致缓存无法复用。
对于出现“网站突然变慢”的问题,也不要第一时间认为GCS性能下降。先判断是CDN命中率下降、源站回源增加、负载均衡延迟增加、GCS读取变慢,还是用户到Google边缘节点之间的网络发生变化。一个正确的监控体系,应该能够把用户请求从边缘节点一路追踪到后端。
如果只是普通企业网站、新闻网站、博客或者软件下载站,最实用的架构其实并不复杂:GCS保存静态文件,外部HTTPS负载均衡器提供统一入口,Cloud CDN负责缓存,DNS指向负载均衡器,资源使用版本化文件名,Cache-Control负责定义缓存生命周期,敏感资源再增加签名访问控制。
这样做的好处,是把存储、计算和分发三个问题彻底分开。GCS不需要为了几百万次重复图片请求不断承担压力,CDN也不需要知道你的业务数据库如何运行。只要资源本身适合缓存,用户访问的越多,边缘缓存发挥的价值反而越明显。
高性能静态资源服务的关键从来不是简单地堆服务器,而是让正确的数据尽可能少地跨越网络。文件长期稳定,就让它长期缓存;文件发生变化,就改变URL;用户距离源站很远,就让边缘节点承担分发;文件涉及权限,就让访问控制负责身份验证。把这几件事情分别做好,GCS与CDN才能真正形成一套既快、又稳定、而且成本可控的静态资源架构。
Google Cloud Storage 与 CDN 如何结合,如何搭建高性能静态资源服务
图片说明:示意图 图片来源:Public Domain(公有领域)
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP