使用Cloudflare worker加速Cloudflare R2访问速度

cloudflare R2 提供了免费10G的对象存储并且兼容Amazon S3 api 操作, 与大多数对象存储提供商不一样的是R2的流出流量是免费的, 不用担心请求被恶意刷爆第二天银行来收房子的情况发生. 但是cloudflare给R2分配的ip都是xxx.xxx.xxx.1形式的ip, 这种ip节点在国内的访问体验很不好, 特别是在移动网络下, 基本无法访问.
然而计算机领域里有句叫'没有什么问题是加一个中间层不能解决的', 而cloudflare里的worker刚好可以当这个'中间层'. worker是cloudflare提供的一个可以运行js/ts代码的serverless容器.

设计原理

我们无法更改cloudflare为R2分配的ip, 但是worker的路由我们是可以配置. 由此我们可以通过优选好的路由连接到worker, 然后让worker作为中间人去帮我们访问R2的资源, worker和r2同属于cloudflare网络, 它们之间的通信会非常快. 这样一来就能实现R2的'加速'访问.
本篇文章演示中R2绑定的访问域名是static.merack.top, worker通过worker路由绑定的域名是cdn.merack.top. 其中cdn.merack.top是最终给用户看到的域名. 用户向cdn.merack.top发出请求调用worker, worker请求R2: static.merack.top, 将R2的数据返回给用户.

创建worker

点击左侧workers和pages, 新建一个hello world 的worker, 不用选其他模板.
然后点击右上角的编辑代码按钮修改为如下代码

const R2_DOMAIN = 'static.merack.top'; // 改成自己的R2公共访问域名

async function handleRequest(request) {
  try {
    const url = new URL(request.url);
    
    // 构建新的R2资源URL
    const targetUrl = new URL(`https://${R2_DOMAIN}`);
    targetUrl.pathname = url.pathname;
    targetUrl.search = url.search;

    // 复制并修改请求头
    const headers = new Headers(request.headers);
    headers.set('Host', R2_DOMAIN);
    headers.delete('Cookie'); // 移除不必要的cookie头

    // 创建新请求
    const newRequest = new Request(targetUrl, {
      method: request.method,
      headers: headers,
      redirect: 'follow'
    });

    let response = await fetch(newRequest);
    return response;
  } catch (err) {
    // 错误处理
    return new Response(err.stack, { 
      status: 500,
      headers: { 'Content-Type': 'text/plain' }
    });
  }
}

// 监听所有请求
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

R2_DOMAIN 常量改成自己的R2公共访问域名, 比如我这里是static.merack.top, 然后点击部署

配置worker路由

点击设置->域和路由, 点击右上方的添加按钮, 类型选择路由

接下来填入需要面向用户的那个域名, 路径填/*, 例如我这里填的就是cdn.merack.top/* 
这样用户访问cdn.merack.top时就触发worker去代理请求到R2, 但是此时还并不是优选的路由, 我们还需要修改下DNS解析 添加DNS记录让cdn.merack.top指向优选好的cloudflare cdn节点ip, 关于cf如何优选ip网上有很多教程就不赘述了, 图方便的话可以直接添加一个cname记录到别人的优选域名, 这里我用的是 cloudflare.182682.xyz , 也可以填我的博客域名www.merack.top , 但是注意后面的小黄云记得关闭, 让其状态为 '仅DNS' 模式, 这样你的cname才有意义.
点击保存后就可以将我们的访问域名换成worker路由的域名看看效果了.

测试

经过一番折腾后效果对比以前有了很大提升, 但还是有些地区无法正常访问, 这取决于当地的网络状况和优选ip的质量. 如果要做到100%的可用性的话, 建议还是花点钱买个好点的服务, 比如Amazon S3 + Amazon Cloudfont, 如果你的域名有备案, 国内云服务商提供的对象存储也是不错的选择.

限制

这个方案的限制主要来自worker. worker的免费订阅最关键的两个限制是CPU时间和日请求额度.

  1. 每个请求的CPU时间是10毫秒, 超过这个时间请求会中断, 因此该方案不适用于大文件下载, 下到一半会中断. 10ms的时间最多能下到多大的文件没有测试过, 我用来请求存储本博客图片资源的R2的中值CPU时间不到1ms, 如果只是用作图片类型的文件加速, 10ms绰绰有余.
  2. worker的每日请求是十万, 因此该方案只能适用于日访问量在万级以下的小博客. 如果你的日访问量都到万级以上了, 应该不缺那点小钱去买个好点存储和cdn了吧. 为了防止脚本恶意刷请求, 可以配合cloudflare waf里的速率限制规格做一定的限制, 关于cloudflare waf我之前有写过文章介绍.

扩展

1.本文例子中的使用场景是主要图片访问加速, 那么就会涉及到缓存时间的问题. worker返回的静态资源响应是会带有缓存控制的响应头(Cache-Control)的
如果你想修改缓存的过期时间, 可以使用worker的cache api来控制, 具体可以参照官方文档: https://developers.cloudflare.com/workers/runtime-apis/cache/ https://developers.cloudflare.com/workers/reference/how-the-cache-works/ 2.虽说本文是以加速r2的访问为主题, 当也可用于加速其他的网站, 只要把代码中的 R2_DOMAIN 常量改改就可以做到. 但如果你代理的是某些版权意识比较看重的大公司的站点, 访问量大了他们可能会向cloudflare投诉, 认为你在进行'假冒官网', '欺骗用户'等欺诈行为, 那么cloudflare可能会封禁你的账号.