Cloudflare Workers 如何重新定义边缘计算
第一次接触 Cloudflare Workers,很容易把它理解成另一种 Serverless。
这也不能算错。
你写一个 fetch(),部署,得到一个 URL。请求进来,代码执行,响应出去。没有服务器需要登录,没有 Kubernetes 集群需要维护,也不必在凌晨两点研究为什么某个 Pod 又变成了 CrashLoopBackOff。
看起来很像 Lambda,只是离用户更近。
但如果只这样理解 Workers,大概就像把 iPhone 理解成一台“没有键盘的 BlackBerry”:描述基本准确,却恰好错过了最重要的部分。
Workers 真正有趣的地方,并不是 Cloudflare 在世界各地放了很多服务器,而是它对一个已经存在几十年的问题给出了不同答案:
运行一段代码,究竟需要多少资源?
一个酒店房间,而不是一套住宅
过去二十年,云计算一直在做一件事情:把计算机切得越来越小。
最早,我们租一台服务器。
后来有了虚拟机。一台物理服务器可以假装自己是很多台计算机。
再后来是容器。我们发现,为了运行一个 Web 服务,似乎没有必要连操作系统也完整复制一份。于是 Docker 出现,Kubernetes 随后把管理这些容器发展成了一门独立学科——以及一个规模可观的就业市场。
Serverless 又往前走了一步:开发者甚至不需要知道容器什么时候启动。
但容器仍然在那里。
Cloudflare Workers 做了一件更加激进的事情:连容器也不要了。
Workers 的基本运行单元是 V8 Isolate。
如果把传统容器想象成住宅,那么每个应用都有自己的厨房、浴室、电表和门锁。它很独立,也很安全,但建造、清洁和维护都需要成本。
Isolate 更像酒店房间。
几百个房间共享一栋建筑的电梯、供水和中央空调,但每个房间仍然有自己的门锁。新客人入住时,不需要重新盖一栋楼,只需要打开一个房间。
这就是 Workers 能够快速启动的根本原因。
一个 JavaScript Worker 不需要先启动 Linux,再初始化容器,然后加载完整运行环境。V8 只需要在已经运行的进程中创建一个新的隔离执行环境。
于是,“冷启动”从一个基础设施问题,开始变成一个近乎可以忽略的运行时问题。
这件事情比听起来重要得多。
因为如果启动一个计算环境足够便宜,你就不再需要提前猜测代码应该运行在哪里。
代码可以等请求出现以后,再决定在哪里执行。
这才是“边缘计算”真正有意思的地方。
但是,大家住在同一家酒店安全吗?
这是 Isolate 架构最容易让人产生不安的地方。
如果很多客户的代码都运行在同一个进程里,那么一家公司的 JavaScript,岂不是距离另一家公司的数据只有几个内存地址?
Cloudflare 为此在 V8 沙箱之外又构筑了多层防线。
其中一个重要概念叫 Pointer Cage。
可以把它想象成在进程的巨大地址空间里划出一块围栏。V8 中的指针不能随意指向整个计算机的内存,而只能在规定的地址范围内活动。即使某段代码出了问题,它面对的也不是一片无限开放的内存空间。
更有意思的是,现代 CPU 本身也开始参与这件事情。
Workers 的安全设计利用硬件级内存保护机制,让某些越界访问不是由 JavaScript、V8,甚至操作系统来“劝阻”,而是直接在处理器层面被阻断。
这代表了一种很有意思的基础设施趋势:
隔离正在从“给每个人一台计算机”,变成“让所有人共享一台计算机,但让他们彼此无法看见”。
前一种方式简单、昂贵。
后一种方式复杂,却极其高效。
云计算过去二十年的历史,某种程度上就是不断寻找这条边界的历史。
时间突然有了两种价格
理解 Workers 的第二个关键,不是内存,而是时间。
在普通人的世界里,100 毫秒就是 100 毫秒。
但在云计算世界里,并非如此。
假设一个 API 总共用了 230 毫秒。
它先花 5 毫秒解析请求,然后等待数据库 100 毫秒;数据库返回后又计算 10 毫秒;接着调用另一个 API,又等了 100 毫秒;最后花 15 毫秒整理结果。
从用户的手表来看:
230ms。
但从 CPU 的角度来看,真正干活的时间可能只有:
30ms。
剩下的 200ms,CPU 基本在等。
这就是 Wall-clock Time 和 CPU Time 的区别。
墙上的钟不会关心你的程序在做什么。程序从开始到结束用了 230ms,它就忠实地走过 230ms。
CPU 不一样。
它只记录自己真正工作了多久。
Workers 的计量哲学很大程度上建立在这个区别之上:等待外部 I/O 与真正消耗计算资源,并不是同一种事情。
这会悄悄改变程序设计。
在传统计算环境中,我们很容易形成一种直觉:
程序运行得越久,成本越高。
在 Workers 世界里,更准确的问题变成:
在这段时间里,我究竟占用了多少计算资源?
这也是为什么异步 I/O 在 Workers 中如此自然。
数据库可以慢一点。
API 可以远一点。
只要 Worker 没有持续占用 CPU 做无意义的事情,“等待”本身并不等价于持续计算。
时间第一次被拆成了两种价格。
128MB:一个看起来寒酸的数字
然后你会遇到 Workers 最令人不舒服的数字:
128MB。
今天,一台普通开发电脑可能有 16GB、32GB,甚至 64GB 内存。手机都有好几个 GB。于是第一次看到 128MB,很容易产生一种回到 1998 年的感觉。
但 128MB 并不是因为 Cloudflare 买不起内存。
它是一种建筑规范。
如果允许每个 Worker 随意占用几个 GB,那么在同一台机器上同时运行大量 Isolate 就会变得困难,快速创建和回收执行环境也会变得昂贵。
所以 Workers 迫使开发者学习一种已经存在很久、却经常被现代服务器掩盖的技巧:
不要拥有数据,让数据经过你。
例如处理一个 10GB 文件。
最直觉的办法是:
下载。
读进内存。
处理。
输出。
在 Workers 上,这种程序可能活不到第二行。
另一种方法是 Streaming。
数据像传送带上的包裹一样经过程序。Worker 每次只看其中一小段,处理,然后让它继续向前。
10MB 是这样。
10GB 也是这样。
理论上,100GB 仍然可以这样。
于是 128MB 从一种限制,变成了一种提醒:
内存并不是仓库,它也可以只是工作台。
很多优秀的分布式系统,恰恰是在资源不够的时候被设计出来的。
AI 又把容器请了回来
事情发展到这里,本来很容易得出一个漂亮结论:
Isolate 会取代 Container。
历史通常不肯如此配合。
到了 2026 年,AI Agent 开始需要做越来越奇怪的事情。
它不仅调用 API,还要运行 Git、编译代码、处理图片、启动浏览器,甚至执行一些几十年前写下来的二进制程序。
这些工作并不适合塞进一个轻量的 V8 Isolate。
于是容器又回来了。
但这一次,它的位置变了。
Cloudflare 正在形成一种很有意思的计算模型:
Isolate 是大脑,Container 是双手。
大多数时候,Agent 待在 Worker 里。
它思考,决定下一步做什么,调用轻量工具,访问数据库,协调状态。
只有真正需要重量级执行环境时,才把任务交给 Container。
这和过去“所有东西都装进一个容器”的思路正好相反。
过去我们先准备一台厨房,然后决定今天做什么菜。
现在我们先决定吃什么,只有真的需要炒菜时,才临时叫来一个厨房。
对于 Agentic Computing,这可能是一种比“Serverless”更值得注意的变化。
未来的应用也许并不是运行在某一种计算环境中。
它会在不同重量级的计算环境之间移动。
轻量决策发生在 Isolate,重量执行发生在 Container,状态则存在另外的地方。
应用开始不像一台机器,更像一个组织。
那么,Worker 到底能活多久?
答案很简单:
不要问。
这是另一个需要忘掉服务器思维的地方。
一个传统应用服务器通常有明确的生命周期。它早上还在那里,下午通常也在那里。我们因此很自然地在内存里保存一些东西。
Workers 不提供这种承诺。
一个 Isolate 可能连续处理很多请求,也可能在完成一个请求之后消失。
这就产生了一个很容易误判的现象。
你写:
let counter = 0
下一次请求进来,发现 counter 居然还在。
再下一次,也在。
于是你开始产生感情。
这是危险的。
Cloudflare 只是在复用一个“温热”的 Isolate,并没有和你签订任何关于永恒的合同。某个时刻,请求可能被送到另一个 Isolate,或者原来的那个被回收。
你的 counter 就消失了。
所以全局变量适合保存配置、客户端对象,以及“消失了也没有关系”的缓存。
真正重要的状态必须住在别处。
KV 适合大量读取、较少修改的数据。
D1 给你熟悉的 SQL 世界。
Durable Objects 则解决更有趣的问题:当许多请求必须围绕同一个状态协调时,怎样让那个状态拥有一个明确的“主人”。
聊天室、协同编辑、游戏房间,都属于这一类。
这背后其实是一条非常重要的原则:
Compute is ephemeral. State is explicit.
计算是短暂的。
状态必须被明确安排。
如果理解了这一点,很多所谓“云原生架构原则”突然会变得非常自然。
Response 已经回去了,但我还有一点事
短暂生命周期还带来另一个问题。
假设用户访问一个页面。
Worker 已经算出了答案。
我们希望尽快把 Response 发给用户。
但与此同时,我们还想记录一条日志,更新缓存,或者发送统计数据。
如果先做完这些事情再返回 Response,用户就要为这些与他无关的工作等待。
如果先返回,然后简单启动一个 Promise,又不能保证 Worker 会继续活着。
于是有了 waitUntil()。
它表达的是一句非常人性化的话:
“客人已经走了,但我还要收拾一下桌子。”
Response 可以先回到用户手里,而 Worker 获得一点额外生命周期完成后台任务。
日志上报是典型场景。
缓存更新也是。
统计分析同样如此。
如果没有 waitUntil(),那些没有被等待的异步任务随时可能随着执行环境结束而消失。
这看起来只是一个 API。
实际上它反映了 Serverless 编程最重要的变化之一:
程序的生命周期,不再等于进程的生命周期。
最后,服务器去了哪里?
学习 Workers 一段时间之后,会出现一个有趣的时刻。
你开始很少思考服务器。不是因为服务器不存在。
恰恰相反。服务器仍然在那里。CPU、内存、SSD、交换机、光纤、机房、电力和散热系统,一个都没有消失。
消失的是它们在程序员脑海里的位置。
你不再首先问:“我要几台机器?”
而开始问:“这段计算需要什么?”
需要 20ms CPU?给它一个 Isolate。
需要等待数据库?让它等。
需要处理 10GB 数据?让数据流过去。
需要保存状态?把状态交给 KV、D1 或 Durable Objects。
需要运行一个庞大的二进制程序?叫一个 Container 来。
完成之后,让它消失。
这可能才是 Cloudflare Workers 最值得理解的地方。
它并没有发明一种更小的服务器。它试图让服务器本身退出应用架构的中心舞台。
过去,我们先拥有一台计算机,然后把程序放进去。
云计算把它变成:先租一台计算机,然后把程序放进去。
Serverless 又把它变成:先写程序,平台替你准备计算机。
而边缘计算正在尝试最后一步:程序出现在哪里,计算就出现在哪里。
至于那台服务器究竟叫什么、在哪里、活了多久——
最好与你无关。
这大概也是“无服务器计算”这个名字最大的讽刺。世界上从来没有更多服务器。只是我们终于可以少想一点它们了。