一个视频播放器想跳到电影中间,客户端只请求几兆字节。但在旧的区间缓存实现下,这一个请求可能让 Cloudflare 从源站拉取 128 MiB,还要在多个数据中心之间搬运——有时等数据搬完,观众早就关掉页面走了。
![]()
区间请求(Range Request)本来就是大文件分发的核心机制。问题出在缓存策略上:在 CDN 规模下,朴素地缓存区间请求,很容易同时浪费网络带宽和磁盘操作。
Cloudflare 因此重写了缓存处理区间请求的方式。对对象存储 R2 而言,p99 首字节时间下降了约一半;后续一项改进又让 R2 及类似负载的分层缓存流量减少了约 6%。如今这套新实现以 Origin Range Requests 的形式开放,所有套餐均可自助开启,没有区间大小需要调,也不用联系客服,用一条 Cache Rule 打开即可。
区间请求到底在请求什么
HTTP 区间请求要的是资源的一部分,而不是整个资源。客户端发一个带 Range 头的普通 GET,服务器若支持,就返回 206 Partial Content,并用 Content-Range 标明返回的是哪一段字节。
这类请求在大对象场景里很常见:播放器请求跳转位置附近的字节,下载管理器在中断后断点续传,包管理器只取构件的一部分,客户端想看看文件末尾而不必先下载前面全部内容。是否发区间请求由客户端决定,Cloudflare 不改这个请求,改的是自己如何满足它。
固定区间大小带来的两难
Cloudflare 多年来通过静态 zone 配置支持区间请求缓存。旧实现类似 nginx 博客里描述的缓存切片:每个客户端请求被映射到文件的固定大小区间,区间大小和文件扩展名按 zone 配置。
区间大小本身就是个两难:区间小,源站请求又多又碎,Cloudflare 还要在内存里合并这些请求,带来内存压力;区间大,Cloudflare 下载的数据远超客户端实际所需。更麻烦的是,区间大小是缓存键的一部分,改一次大小就会让缓存条目全部失效。
这套设计还让经由其网络取文件的人体验糟糕。落在同一区间内的并发请求,不管各自客户端要的区间隔得多远,都可能互相阻塞。基于 nginx 的缓存也无法把区间缓存和并发流式传输结合起来——后者本是为了让多个客户端在文件还在写入时就能从缓存读取。两者同时开启会截断响应,于是每个区间必须完整下载完才能被读取,热门文件每段区间都要单独回源一次。
多出来的字节还会穿过分布式缓存的多个环节:从源站到一个数据中心,在缓存层级之间,再发往客户端。每一跳都在放大浪费。没有任何一个区间大小能同时适配小跳转、顺序下载、大媒体文件以及各种源站与缓存拓扑。
内部缓存的局限就这样直接暴露给了客户,而客户本不该为了文件被高效分发去深入理解这些能力。
把缓存块大小直接当成区间大小
解法来自 Pingora 已经在做的事。为了便于中断下载续传,它把每个缓存文件按固定大小的块存到磁盘上。既然缓存本来就按块思考,区间大小就不必再是一个配置项——直接用块大小就行。
收到区间请求时,Cloudflare 先检查请求的字节里哪些已经缓存,直接返回;缺失的部分,按缓存固定块边界对齐去源站取,结果存进与普通缓存存储相同的块里,最终只把客户端要的字节返回。比如请求 150-250 字节,可能触发拉取包含这些字节的对齐块,但下行响应里仍然只有 150-250。
这并没有消除小于块大小的请求的额外工作——这类请求仍可能要拉取包含它的整个块。非对齐区间也带来了更多工作量。区别在于,这些额外工作是有界且可预测的,不再由客户选择的区间大小决定,也就不会对 Cloudflare 和源站造成不可预料的后果。
拼出一个正确响应有多难
解析 Range 头并不难。难的是当请求的字节分散在已缓存数据、源站响应和正在进行的下载之间时,如何产出一个正确的响应。
边缘节点大致要做这些事:
- 查询资源头部和区间覆盖元数据;
- 从磁盘或内存中的流读取能读到的部分;
- 用对齐区间从源站补齐缺失覆盖;
- 写入新字节,并在元数据中记录已完成的区间;
- 合并各片段,为客户端响应设置 Content-Range 和 Content-Length。
这是一个协调者,而不是一次简单的缓存查找。一个请求可能同时用到已缓存的前缀、正在进行的下载和新取回的后缀。内部源站响应可以比下行响应更大,但下行的状态码和头部只描述被请求的那段区间。如果已有下载覆盖了另一个请求的字节,第二个请求可以直接挂到那个下载上,不必再发起一次回源,这在热门大文件突发访问时共享了工作。
组合之前还要先校验:所有字节必须属于同一个表示。协调者会检查每个源站响应是否匹配请求区间、资源总大小是否稳定、实体标签在各子请求间是否一致。If-Range 头让客户端只在表示仍匹配已知校验器时才请求区间;不匹配时,Cloudflare 返回完整表示,而不是把不同版本的字节拼在一起。对于即将解压的表示,Cloudflare 也避免组合区间,因为压缩字节上的区间与解压后响应选中的区间并不相同,这种情况下它直接返回转换后的完整响应。
最终行为仍是标准 HTTP:可满足的区间返回 206 和准确的 Content-Range;不可满足的区间返回 416 Range Not Satisfiable;无法安全按区间返回的响应,就作为完整表示发出。
生产环境里变了什么
在向所有人开放之前,Cloudflare 先把旧区间缓存功能的现有用户迁到新实现,从对自己伤害最大的负载开始。这些负载对超大文件有大量并发区间请求,流量往往很尖。旧系统上配置的区间大小从几十 KB 到几十 GB 不等。
区间配得大,一个连软盘都填不满的请求,可能让 Cloudflare 从源站下载几张 DVD 容量的数据,再跨多个数据中心搬运,有时客户端早已放弃离开。区间配得小,一个请求就变成一长串微小的源站请求,每个都在等待时占着内存。两种模式都造成过真实的运维事故。
R2 是最大的内部用户,其配置的区间大小为 64 MiB,一次跨区间边界的小读取可能让 Cloudflare 在数据中心之间传输 128 MiB。迁到新实现后,R2 各存储区域的 p99 首字节时间在缓存命中和未命中上都下降约一半。最近一项改动让协调者把磁盘上已有的字节与上游取回的字节合并在一个响应里——此前一个请求要么全走缓存,要么全走上游——为 R2 开启后,R2 及类似负载的分层缓存流量周环比下降约 6%,相当于每天少搬几 PB 数据。
怎么开
缓存不再依赖客户选择的区间大小之后,产品配置简单到不能再简单:客户通过 Cache Rules 选中匹配流量,分块和缓存协调交给 Cloudflare。
它面向的是客户端天然会请求大对象局部的流量:
- 可拖动进度的渐进式视频,如 MP4;
- 可续传的大文件下载;
- 软件分发与包仓库;
- 磁盘镜像、归档及其他数 GB 级对象;
- 传输大模型文件或读取模型构件局部区域的 AI 训练与推理负载;
- 只取元数据或大文件中一小块区域的客户端。
Cloudflare 打算让这成为某些文件类型的默认分发方式,并可通过缓存规则修改。该功能不改变常规缓存资格:开启它不会让本来不可缓存的响应变得可缓存;源站也需要支持字节区间,部分回源才有意义。开启方式是在仪表盘的 Caching > Cache Rules 下创建规则,或通过 API 设置 origin_range_requests 为 on。客户选流量,边缘负责缓存协调与正确性。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.