cloudflare-os:用Cloudflare全家桶搭一座零维护的个人云操作系统
看到 cloudflare-os 这个名字,你可能会以为是哪个硬件厂商放出来的操作系统镜像。其实不是,它是我自己折腾了大半个月的一套个人云基础设施方案:不买服务器、不装 Linux,也不碰 Docker,纯靠 Cloudflare 全家桶(Workers、Pages、R2、D1、KV、Queues)把一套完整的应用运行环境搭起来。叫它 os,是因为从开发者的视角看,这套东西真的就像一台远程的“个人电脑”——有计算、有存储、有数据库、有后台任务调度,只不过它跑在全球边缘网络上。
这篇文章就是这套 cloudflare-os 方案的完整复盘。我会把组件选型的思路、核心代码的写法、部署时容易踩的坑全部拆开讲。适合谁看?如果你是个独立开发者,厌倦了维护服务器;或者你手上有不需要重型后端的小项目,想找一个近乎零维护的托管方案;又或者你只是想搞清楚 Cloudflare 这套生态到底能拼出什么花样,都可以从这篇文章里找到想要的东西。我不承诺它能替代所有架构,但至少能告诉你:在什么场景下,把 Cloudflare 当成一台“操作系统”来用,是真的香。
1. 项目概述:cloudflare-os 到底在做什么
1.1 为什么叫“OS”而不是“网站”或“服务”
我一开始只是想在 Cloudflare Workers 上部署几个小工具,结果越玩越觉得不对劲:单拎出来每个都是“函数”,但把它们组合在一起,体验上已经非常接近一台完整的计算机了。
传统意义上,操作系统管的是进程、文件、内存、外设。你对照着看 cloudflare-os 这套架构:
- Workers 是无状态计算节点,天然就是“进程”的载体,请求进来,函数执行完就退出,谁来都一样。
- R2 是对象存储,等价于硬盘;KV 是全局键值存储,等价于系统注册表和配置文件目录。
- D1 是嵌入式 SQLite 数据库,相当于本地数据库文件,只不过它在边缘侧分发。
- Queues 是消息队列,相当于操作系统里的任务队列,把异步任务排进去慢慢消化。
- Cron Triggers 是定时器,对应操作系统的定时任务,每天凌晨该跑的数据清理、统计汇总,交给它就行。
- Pages 则承担了“桌面环境”的角色,静态资源、前端框架、SSR 渲染都能在这层搞定。
当你把这几层拼到一起,就会发现:你不再需要关心服务器在哪儿、磁盘满没满、进程崩了怎么重启。你只需要写代码,然后wrangler deploy,剩下的资源调度、容错、扩容都是平台兜底。这体验,确实更像在用一台远程操作系统,而不是在租服务器。
1.2 这套方案适合谁,能解决什么问题
我先泼一盆冷水:cloudflare-os 不是万能的。它适合的场景非常明确。
首先是个人网站、博客、作品集。静态资源放 Pages,评论和阅读量统计走 D1,小功能用 Workers 实现,身份验证交给 KV 存会话。整个项目零服务器成本,而且全球访问速度不差。
其次是 API 服务和小型 SaaS 的后端。比如你做了一个链接短链服务、文件分享工具、订阅管理后台,这类业务逻辑不重,但需要有数据库、有存储、有定时任务。用 Workers + D1 + R2 + Cron Triggers 能把全套后端逻辑塞进去,一个月在免费额度内基本不花钱。
再者是工具的聚合层。程序员都喜欢收集在线工具,JSON 格式化、时间戳转换、正则测试……这些以前要么写在本地,要么靠第三方网站。用 cloudflare-os 的思路,你可以把它们统一挂在一个域名下,共享一个鉴权中间件、一套静态资源、一个配置中心,管理起来非常舒服。
那它解决的核心痛点是什么?一句话:把“运维”这个环节从你的生活里删除。不用 SSH、不用盯监控、不用半夜爬起来处理磁盘告警。你只需要关心业务代码本身。我实际用了快一个月,最强烈的感受是:我几乎忘了“部署”这回事,改完代码敲一条命令,几十秒后全球生效,这种体验一旦习惯了,很难再回去扛服务器。
2. 核心组件选型:Cloudflare 生态的角色分工
2.1 Workers:计算层,相当于 CPU
Workers 是整个 cloudflare-os 的执行引擎。它跑的是 V8 引擎的 JavaScript/TypeScript,采用“isolate”模型,而不是传统的容器。每次请求进来,平台会选择一个边缘节点,在你的代码里执行完请求处理逻辑,然后销毁环境。
“无状态”这三个字是关键。你写的 Worker 不能假设内存里有任何持久数据,每次调用都可能落到不同节点。所以你在设计时要默认:任何需要跨请求保留的东西,要么放 KV,要么放 R2,要么放 D1。这也意味着,如果你以前习惯在进程里维护一个全局连接池,或者用内存缓存用户登录态,这套思路在 Workers 上必须先纠正过来。
选择 Workers 而不是自建 Node.js 服务的理由也很直白:一是冷启动极快,据我实测常规请求的额外延迟通常在个位数毫秒级别;二是全球节点自动就近响应,不用自己规划多区域部署;三是配额内免费。它不适合的是 CPU 密集型任务,比如大文件图片处理、复杂计算,这类场景会撞到执行时长限制,后面我会细说。
2.2 KV 与 R2:存储层,相当于注册表和硬盘
KV(Key-Value Storage)是 Cloudflare 最老牌的存储产品。它本质是一个全球复制的键值存储,读极快,写有延迟,数据最终一致。所谓“最终一致”,就是你写入一个 key 之后,可能过几秒才能在全球所有节点读到。所以 KV 合适的用法是:存储配置项、功能开关、用户会话、不敏感但需要频繁读的内容。
R2(Cloudflare R2)则是对标 S3 的对象存储,专门放“大块头”:图片、视频、压缩包、导出文件。它的杀手锏是出口带宽零收费。我用过 S3 的人都知道,云厂商最肉疼的就是流量费,R2 直接把这个成本砍掉了,对个人项目特别友好。
在我的 cloudflare-os 里,KV 承担的是“系统注册表”的角色。比如多租户的配置、API 密钥的加密版本、各个子服务的路由开关,都丢在 KV 里,代码启动时读一次。R2 承担的是“用户目录”的角色。用户上传的头像、导出的 CSV、生成的 PDF,全部按users/{userId}/{filename}的路径结构存。这样分层的思路,和操作系统的“配置文件放 etc,用户数据放 home”其实是异曲同工。
2.3 D1 与 Hyperdrive:数据层,相当于内存数据库
D1 是 Cloudflare 基于 SQLite 构建的全球分布式数据库。刚推出的时候很多人持观望态度,但这大半年用下来,它的稳定性已经比我预期好太多了。D1 的核心价值是:你可以用 SQL 语言写业务逻辑,不用像用 KV 那样一切都要自己维护索引和关联。
我用 D1 存的是结构化业务数据:用户表、文章表、订单记录、操作日志。它的语法就是标准 SQL,后端代码写起来和以前用 MySQL/Postgres 的体验几乎一样,无非是把原来的连接字符串换成了 binding(绑定)。
Hyperdrive 是给那些必须连接外部数据库(比如 Postgres 或 MySQL)的场景用的加速层。它的思路是在边缘侧维护连接池,复用数据库连接,避免每次请求都重新建立网络连接。如果你的项目里还有一个托管在别处的旧数据库,Hyperdrive 可以让 Workers 访问它时快不少。但纯新建项目,我建议直接 D1,省心。
2.4 Pages、Queues、Cron Triggers:应用托管与后台调度
Pages 是 Cloudflare 的静态站点托管平台,支持直接对接 Git 仓库自动构建部署,也支持在站点目录下放functions/来写后端 API。它和 Workers 的关系有点微妙:Pages 的 Functions 底层也是 Workers,但更偏向“站点优先”。
Queues 是异步消息队列。你在请求里不想同步处理的任务,比如发通知邮件、更新搜索索引、生成缩略图,可以直接塞进队列,由消费者 Worker 逐步处理。它解决的是“任务的削峰填谷”,避免了用户在请求链路上等待重操作完成。
Cron Triggers 是定时任务,和系统里的 crontab 一个意思。你可以在wrangler.toml里配置[[triggers]],指定crons = ["0 3 * * *"]之类的表达式,到点平台就会自动调用你的 Worker。我用它做每日数据备份、过期文件清扫、定时拉取第三方数据。这四类组件合在一起,才让 cloudflare-os 真正“完整”起来——你有计算、有存储、有数据、有异步处理、有定时调度,这不就是一台操作系统的全部能力吗?
3. 实操过程:从零搭一个最小可运行的 cloudflare-os
3.1 环境准备与项目初始化
先说前置条件:你需要一个 Cloudflare 账号,以及 Node.js 18+ 的本地环境。CLI 工具是 Wrangler,现在官方推荐用npm create cloudflare来初始化项目。
npm create cloudflare@latest cloudflare-os按提示选择 Worker 模板即可,不用选带数据库的复杂模板,后面我们手动加。初始化完成后,项目里最关键的文件是wrangler.jsonc或wrangler.toml,这是 Wrangler 的配置文件,相当于你“操作系统”的启动参数。
我建议在项目目录下建立清晰的模块结构,因为后续组件会越来越多:
cloudflare-os/ ├── src/ │ ├── index.ts # 入口,路由注册 │ ├── middlewares/ # 鉴权、日志、CORS │ ├── routes/ # 各业务模块路由 │ ├── services/ # 与 KV/R2/D1 交互的逻辑 │ └── utils/ # 工具函数 ├── static/ # 前端静态资源(用于 Pages) ├── migrations/ # D1 数据库迁移 SQL ├── wrangler.toml └── package.json这个结构看起来简单,但对后期维护很关键。因为 cloudflare-os 的本质是“多个小服务拼成一个大系统”,如果你从一开始就让所有代码堆在index.ts里,不出两周就会乱成一锅粥。
3.2 写“内核”:基于 Hono 的统一路由与鉴权
Workers 原生支持直接导出fetch处理函数,但直接把裸函数当内核用,路由、中间件、参数解析都要手写,太费劲。我选的是 Hono ,一个为边缘运行时设计的极简 Web 框架,API 风格类似 Express,非常轻量。
先安装依赖:
npm install hono然后写入口文件src/index.ts:
import { Hono } from 'hono' import { authMiddleware } from './middlewares/auth' import { loggingMiddleware } from './middlewares/logger' import { api } from './routes/api' import { files } from './routes/files' type Bindings = { CONFIG_KV: KVNamespace FILES_BUCKET: R2Bucket DB: D1Database API_TOKEN: string } const app = new Hono<{ Bindings: Bindings }>() // 全局中间件:所有请求都记录日志 app.use('*', loggingMiddleware) // 挂载子路由 app.route('/api', api) app.route('/files', files) // 健康检查端点 app.get('/health', (c) => c.text('ok')) // 404 兜底 app.notFound((c) => c.json({ error: 'not found' }, 404)) export default app中间件我单独抽出来,其中一个核心就是统一的鉴权中间件。它做的事情很简单:读取Authorization头,校验 token 是否与 KV 里存的一致,不一致直接返回 401。
// src/middlewares/auth.ts import { createMiddleware } from 'hono/factory' export const authMiddleware = createMiddleware(async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '') if (!token) { return c.json({ error: 'missing token' }, 401) } // 从 KV 读取当前有效的 token 列表 const validTokens = await c.env.CONFIG_KV.get<string[]>('valid_tokens', 'json') if (!validTokens || !validTokens.includes(token)) { return c.json({ error: 'invalid token' }, 401) } await next() })这个设计你可以理解成操作系统的“用户态与内核态”划分:鉴权、日志这类横切关注点放在中间件层,业务代码只关心自己的逻辑,不需要重复判断身份。后续新增模块时,只要在路由定义时挂上authMiddleware,接入成本几乎为零。
3.3 挂载“文件系统”:KV 配置 + R2 对象存储
接下来配置存储绑定。打开wrangler.toml,添加 KV namespace 和 R2 bucket 的定义:
name = "cloudflare-os" main = "src/index.ts" compatibility_date = "2025-01-01" [[kv_namespaces]] binding = "CONFIG_KV" id = "你的KV命名空间ID" [[r2_buckets]] binding = "FILES_BUCKET" bucket_name = "cloudflare-os-files"创建 KV namespace 和 R2 bucket 的命令分别是:
wrangler kv namespace create CONFIG_KV wrangler r2 bucket create cloudflare-os-filesKV 的用法非常直接。初始化的时候,往里面写入一份配置 JSON:
wrangler kv key put --binding=CONFIG_KV "valid_tokens" '["token_a", "token_b"]' wrangler kv key put --binding=CONFIG_KV "site_config" '{"name":"cloudflare-os","maintainer":"yourname"}'在 Workers 代码里读取:
const siteConfig = await c.env.CONFIG_KV.get('site_config', 'json')R2 的上传下载也简单。下面这段代码实现了一个简单的“用户文件上传 + 列表查询”接口:
// src/routes/files.ts import { Hono } from 'hono' import { authMiddleware } from '../middlewares/auth' export const files = new Hono() // 上传文件 files.post('/upload', authMiddleware, async (c) => { const userId = c.req.header('X-User-Id') || 'anonymous' const formData = await c.req.formData() const file = formData.get('file') if (!file || typeof file === 'string') { return c.json({ error: 'no file provided' }, 400) } const key = `users/${userId}/${Date.now()}-${file.name}` await c.env.FILES_BUCKET.put(key, file.stream(), { httpMetadata: { contentType: file.type }, }) return c.json({ key }) }) // 列出用户的所有文件 files.get('/list', authMiddleware, async (c) => { const userId = c.req.header('X-User-Id') || 'anonymous' const listed = await c.env.FILES_BUCKET.list({ prefix: `users/${userId}/` }) return c.json(listed.objects.map((obj) => obj.key)) })这里有一个我在实操里踩过的坑:R2 的list操作返回的是对象元数据列表,不包含内容。如果你需要逐个下载,还需要再对每个 key 执行一次get。所以如果你的业务经常要“列出所有文件并展示体积、上传时间”,建议在 D1 里维护一份文件索引表,R2 只当仓库,别频繁去 list。
3.4 接入“数据库”:D1 建表与查询
D1 的绑定配置同样写在wrangler.toml里:
[[d1_databases]] binding = "DB" database_name = "cloudflare-os-db" database_id = "你的D1数据库ID"创建 D1 数据库并建表:
wrangler d1 create cloudflare-os-db wrangler d1 execute cloudflare-os-db --local --file=./migrations/001_init.sql001_init.sql示例:
CREATE TABLE IF NOT EXISTS files ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, file_key TEXT NOT NULL UNIQUE, file_name TEXT NOT NULL, size INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL DEFAULT (unixepoch()) ); CREATE INDEX IF NOT EXISTS idx_files_user_id ON files(user_id);在 Worker 里查询时,用 prepared statement 避免 SQL 注入:
const { results } = await c.env.DB.prepare( 'SELECT id, file_key, file_name, size FROM files WHERE user_id = ? ORDER BY created_at DESC' ).bind(userId).all()D1 的调用方式和传统 SQLite 很接近,唯一要适应的是它的网络延迟。虽然 Cloudflare 对 D1 做了边缘缓存,但写操作依然要走主节点,所以不要在你的请求热路径上频繁执行写入。我自己的做法是:写操作尽量合并,能用一条语句更新多个字段就绝不发多次请求;读操作如果对实时性要求不高,就把它放到 KV 缓存一层,降低 D1 的读压力。
3.5 部署上线与自定义域名
一切就绪后,本地先验证:
wrangler devwrangler dev会起一个本地服务,你可以直接curl http://localhost:8787/health测试。注意:本地开发时 D1、R2、KV 默认操作的是本地模拟环境,也就是说你在本地写入 R2 的数据不会出现在线上,这是正常现象,别慌。
确认没问题后部署:
wrangler deploy部署完成后,你会拿到一个*.workers.dev的域名。这个域名虽然能用,但生产环境最好挂上自己的域名。方法是在 Cloudflare 控制台进入你的域名,添加一条 DNS 记录,类型选 CNAME 或 AAAA(取决于你的 Worker 支持情况),目标填你的*.workers.dev地址。然后回到 Workers 的页面,在“自定义域”里绑定你刚加的子域名,等待证书自动签发即可。
我自己踩过一个特别无语的坑:绑定了自定义域后,访问一直 502。排查半天发现是 SSL/TLS 加密模式设置成了“灵活”,导致边缘到 Worker 的链路请求头里带了错误的重定向信息。解决办法是把 SSL/TLS 模式改成“完全”(严格也可以),问题立刻消失。这个细节如果你以后遇到,可以先检查这里。
4. 架构细节与设计取舍
4.1 为什么不用自建服务器
这个问题我认真想过。自建服务器的优势是“掌控力”:想装什么就装什么,内存大、磁盘大、跑起任务来毫无顾忌。但它也有一个无法回避的成本——注意力。
独立开发者的注意力是最稀缺的资源。你花在补安全补丁、处理磁盘 IO 升高、重启被 OOM 杀掉的进程上的每一分钟,本来都可以用来写业务代码。cloudflare-os 的模式把这个负担外包了:平台方负责硬件、网络、扩容、安全,我只负责业务逻辑和边界情况。
当然,掌控力也有代价。如果你需要特定的运行时、特定的系统库,或者你要跑一个完整的大型框架(比如完整版 Django、Rails),Workers 的模型会非常难受。所以我的建议是:新项目如果业务逻辑能拆成轻量函数,就大胆用 cloudflare-os;如果你明确知道自己需要一台传统服务器,那就别硬套。
4.2 成本模型与免费额度
我知道大家最关心的就是钱的问题。下面是 Cloudflare 官方开发者计划(免费版)目前的主要配额,我从今年实际使用的情况来看,个人项目基本摸不到上限:
| 资源 | 免费额度 | 我一个月实际用量 |
|---|---|---|
| Workers 请求 | 100,000 次/天 | 约 8,000 次/天 |
| Workers CPU 耗时 | 10ms/请求(免费套餐) | 平均 3ms |
| KV 读/写/删除 | 100,000 次读/天 | 约 15,000 次 |
| R2 存储容量 | 10 GB | 约 2 GB |
| R2 出口流量 | 不计费 | 不好统计,但很省 |
| D1 读/写 | 5GB 存储,500万行读/天 | 远没到 |
“CPU 耗时 10ms”这个限制最容易被忽略,也是最容易撞墙的。一旦你的 Worker 处理一个请求超过 10ms(免费套餐),平台会直接中断执行。所以代码里凡是涉及大体积 JSON 解析、复杂的循环、多次外部请求串行调用,都要小心。我自己后来把几个重业务切到了付费套餐($5/月),CPU 限制会宽松很多,但对大多数人来说,免费套餐已经足够跑个人服务。
4.3 方案的边界与升级路径
cloudflare-os 有明显的边界,我需要诚实地说清楚。
第一,不适合长时间运行的任务。Workers 有执行时长上限,你不能在里面跑一个半小时的批处理。遇到这种需求,正确的做法是拆碎成队列任务,用 Queues 分发给多个 Worker 分别处理,或者干脆把重任务交给其他平台。
第二,不适合超大对象处理。R2 可以存大文件,但 Worker 的内存和处理能力有限。比如你想在 Workers 里直接生成一份几百 MB 的导出文件再传到 R2,大概率会在执行中途被掐断。正确的路径是先产生缩减后的文件流,或者借助 R2 的分片上传功能。
第三,不适合强一致性的复杂事务。D1 支持事务,但毕竟是分布式实现,跨区域的事务延迟你会有感知。如果你的业务要求严格的事务隔离级别,或者需要复杂的多表 join 和海量数据分析,D1 不是合适的选择。
那升级路径是什么?我的建议是“逆向分层”:先把 R2/KV 这类存储留在 Cloudflare,把计算密集、冷启动敏感、依赖特定运行时的模块迁移到 VPS 或容器平台;再把数据库迁到独立的托管数据库,用 Hyperdrive 把查询延时段拉低。也就是说,cloudflare-os 完全可以作为你基础设施的“前端层 + 边缘加速层”,而不是被替换掉。这套方案最大的价值就是它不锁死你,组件之间边界清晰,随时可以抽换。
5. 常见问题与排查技巧实录
5.1 Wrangler 部署失败:配置文件写错别瞎猜
我遭遇过最频繁的部署失败,基本都是配置文件导致的。常见症状是wrangler deploy报Invalid binding type或Could not contact the service。
第一个问题多半是 KV namespace id 写错了。注意wrangler kv namespace create返回的输出有id和binding两个字段,你要把id填到[[kv_namespaces]]的id位置,而不是把整个对象复制进去。
第二个问题偶尔是网络波动或者账号登录态过期,先执行wrangler login重新登录一次,再部署看看。如果还不行,把node_modules和wrangler.toml里的兼容日期调到最新再试。
5.2 自定义域名一直 502,先查 SSL 模式
这个我之前提过。502 或者“重定向过多”基本都是 Cloudflare 边缘到你 Worker 之间的 SSL 握手出了问题。一般处理顺序是:先确认 DNS 记录已经代理(橙色云朵)开启;再进域名的 SSL/TLS 设置,把模式改成“完全”;最后等证书生效,通常一分钟内就能恢复。还有一个隐蔽问题:如果你的 Worker 绑定了多个域名,而你设置的请求头Host和域名不匹配,也会出现访问异常,这时可以检查路由规则里的 Host 匹配。
5.3 D1 本地执行的坑:--local和线上不同步
D1 有一个非常容易误会的地方:本地wrangler dev用的是本地 SQLite 文件,线上wrangler deploy用的是远程数据库。你本地执行了迁移,不等于线上也执行了。正确流程是:
wrangler d1 execute cloudflare-os-db --remote --file=./migrations/001_init.sql如果不加--remote,默认操作的是本地库;线上查不到表的时候,百分之百是漏了这一步。我刚开始用 D1 的一周里,至少因为这个原因怀疑过三次数据库是不是坏了。
5.4 冷启动和性能优化:把能并行的请求并行掉
Workers 的冷启动其实已经很快,但如果你在请求里串行调用了三个外部 API,耗时就会被放大三倍。优化手段很简单:用Promise.all并行。
const [userInfo, fileList, config] = await Promise.all([ fetch('https://api.example.com/user-info'), c.env.FILES_BUCKET.list({ prefix: 'users/1/' }), c.env.CONFIG_KV.get('site_config', 'json'), ])另外,能不下发的依赖就不要打包进去。Wrangler 默认会把node_modules里的依赖打进 bundle,如果你只用到了hono,那没问题。但如果你塞了一个巨大的依赖库,只在某条边缘逻辑里用了其中一个小函数,最好自己实现或者拆出去,减小 bundle 体积能明显提升冷启动速度。
5.5 成本与配额失控预警
免费额度够用,但“够用”不等于“不用管”。我见过有人把日志全部打到 KV 里,一个月写入几百万次,然后被平台邮件提醒。排查成本问题,建议先看 Cloudflare 控制台的“Analytics”页面,里面有各个绑定的使用量图表。如果某个数字突然暴涨,优先怀疑自己的代码是不是在循环里调用存储,或者被爬虫大量刷接口。
我的经验是:对外不设防的接口,一定要在 Workers 最外层做频率限制。下面是基于 KV 计数的一个极简限流中间件:
export const rateLimitMiddleware = createMiddleware(async (c, next) => { const ip = c.req.header('CF-Connecting-IP') || 'unknown' const key = `ratelimit:${ip}:${new Date().toISOString().slice(0, 13)}` const count = parseInt((await c.env.CONFIG_KV.get(key)) || '0') if (count >= 100) { return c.json({ error: 'too many requests' }, 429) } await c.env.CONFIG_KV.put(key, String(count + 1), { expirationTtl: 3600 }) await next() })这段代码的作用是以 IP 加小时为维度,限制每小时最多 100 次请求,避免被恶意刷爆 KV 配额。虽然简单,但它能拦下绝大多数爬虫。关键点是给 KV 设置expirationTtl,让计数自动过期,否则 KV 里的碎 key 会越积越多,最后全变成存储成本。
5.6 实战中的其他琐碎踩坑
最后列几条零碎但很实用的经验,都是常规文档里不会写的:
- 使用
wrangler dev --remote可以在本地调试时直接操作远程的 KV/R2/D1,很适合排查“本地好好的,线上出问题”的场景,但注意线上数据会被真实读写,谨慎操作。 - KV 的
get有 1 MB 的值大小限制,R2 单文件最大支持 5 GB(付费版更高),但 Workers 内存每次请求默认上限是 128 MB。设计上传功能时,如果文件很大,要优先考虑流式上传,而不是先把整个文件读进内存。 - Cron Triggers 触发的是普通的 Worker 请求,所以定时任务里如果调用了外部接口,同样要处理超时和重试。触发间隔最短可以到 1 分钟,但免费套餐的 Worker 有 CPU 时长限制,太密集的定时任务容易白跑浪费配额。
- 如果你把前端页面放在 Pages,把 API 放在 Workers,注意跨域问题。Workers 的
OPTIONS预检请求不会被你的路由显式处理时,默认可能返回 405。正确做法是在 CORS 中间件里提前处理OPTIONS请求并返回相应的Access-Control-Allow-Origin头。
我个人在实际操作中最大的体会是:cloudflare-os 这套思路真正改变的,不只是部署方式,而是你规划项目时的心智模型。以前写后端,我第一反应是“我要开一台什么配置的服务器”“数据库装在哪个区域”,现在我的第一反应变成了“这个功能可以拆成 Workers 的哪个入口”“这数据应该放 KV、R2 还是 D1”“这个异步任务要不要丢进队列”。当你习惯了用资源边界来设计代码,而不是用机器规格来设计代码,做出来的系统天然就具备分布式和扩展性。
这套方案后续还可以继续扩展的方向也很多,比如在 Worker 里接入 AI 网关做内容分析,用 Queues 做多步骤发布流水线,甚至把多个边缘函数组合成“云函数工作流”。如果你也想拿自己手头的小项目试试水,我的建议是从最小闭环开始:一个能读写的短链服务,加上一个文件上传下载接口,用不了两个晚上就能全部跑通。跑通之后,你大概就能理解我为什么愿意把这套东西叫成一台“操作系统”了。