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时间和日请求额度.
- 每个请求的CPU时间是10毫秒, 超过这个时间请求会中断, 因此该方案不适用于大文件下载, 下到一半会中断. 10ms的时间最多能下到多大的文件没有测试过, 我用来请求存储本博客图片资源的R2的中值CPU时间不到1ms, 如果只是用作图片类型的文件加速, 10ms绰绰有余.
- 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可能会封禁你的账号.