一个Cloudflare Worker,一个fetch处理函数,一个路由。这是大多数边缘部署的起点,也是我参与构建的那个边缘平台的起点。它承载着数十万个租户账号的Web流量,每个租户都是独立客户,有独立配置、相互隔离,背后还有多个功能团队。
问题在于,位于大型SaaS产品前方的Worker不会一直保持小巧。它会不断累积职责:图片优化、故障转移页面、路由、请求标头和Cookie重写、按租户配置查询。每一项都是合理的边缘侧能力,且大多由不同团队维护。最初的单个文件,慢慢变成所有人都在改、却没有团队全权负责的共享单体。
![]()
这和十年前应用单体的形成是同一股力量。但在边缘场景下,它的危害更严重——Worker处在所有请求的同步路径上,在这里每增加1毫秒耗时,所有用户都会多等1毫秒。
单体在边缘会放大成可用性风险
只有一个Worker,就只有一个部署。任何改动都会重新部署整个脚本,所有团队的发布节奏由最慢的变更决定。一行请求标头修复,要等一个尚未完成的实验功能,然后两者一起发布。回滚一个功能,就是回滚所有功能。
更麻烦的是故障爆炸半径。Worker不同于负载均衡后端那些可替换的实例,它本身就在请求路径中。一个未处理的异常、一个糟糕的正则表达式,或某个功能的热点循环,拖垮的不只是那个功能,而是所有功能和所有租户。多租户架构下,一次糟糕的部署会演变成波及整个客户群的并发事故。
还有平台限制。Worker存在硬性资源上限:每个请求有固定CPU限额和脚本大小上限。每个功能的依赖项都会导致共享代码包膨胀,并在热路径中增加解析和启动工作——即使请求根本不会执行这部分代码。多个团队共享一份资源配额,彼此看不到对方的资源消耗。
权责错配同样明显。故障转移逻辑很少改动,图片处理流水线却需要快速迭代。强制两者写在同一个文件里,就会出现大量合并冲突、所有权不清晰和“恐惧式开发”:没人敢改这个文件,生怕一不小心破坏其他逻辑。
拆分的前提:服务绑定让调用近似函数调用
解决单体问题最直观的办法就是拆分,而最直接的反对理由是成本。把后端单体拆成多个服务,换来了隔离性,代价是增加了网络跳转——每次内部调用都要DNS解析、TLS握手和之前没有的往返延迟。边缘的核心意义是从请求路径上削减请求耗时,这笔开销看起来无法承受。
但在Cloudflare上,这种看法是错的。原因在于服务绑定。服务绑定是Worker之间的调用,Cloudflare会在同一台机器、同一个隔离沙箱内完成调度,没有那些网络开销。调用写法和fetch()一样,但开销近似普通函数调用。正是这一点,让边缘侧的模块拆分具备可行性。
这个模式是一个前置的精简网关Worker,加上一组单一用途的功能Worker。网关只拥有两样东西:组合逻辑(判断当前请求要启用哪些功能、执行顺序)和全局横切关注点(请求预处理、请求标头规范化、可观测性)。它不包含功能逻辑。每个功能Worker只拥有一个关注点、自己的依赖包、测试套件和部署流程。
网关与每个功能之间的契约分为两部分。如果某个功能不适用于当前请求,就不会触发绑定调用。每个功能向外暴露一个轻量的判断函数shouldApply(),网关直接内联执行;Worker仅在这个函数判断需要为真时才通过服务绑定调度。内联决策,远程执行。
网关按固定顺序编排功能。故障转移这类熔断逻辑会在访问源站之前提前拦截,然后执行请求预处理。图片优化可能接管源站拉取逻辑。最后,响应阶段的拦截器负责处理只能在响应里才能判断的条件。
网关只持有每个功能的Fetcher句柄,而不是引入功能代码。这带来两个好处:各个Worker的依赖和CPU配额完全隔离,网关包里只有少量判断逻辑,刚好解决前面提到的平台资源限制问题;并且,某个功能Worker出错或者超时,不能阻断整个请求。一旦功能Worker发生异常,网关直接返回未经转换的源站原始响应,而不是返回错误页面。
隔离不是免费的,代价是协调成本
如果每个功能Worker单独放在代码仓库里,共享契约就会漂移,集成测试要拼接多个代码仓库的构建产物,对网关接口的一次变更会变成一次多仓库迁移。所以网关和所有功能Worker被放在同一个单体代码库中。包边界让每个Worker保持独立的构建和部署目标,网关只能导入每个功能对外暴露的接口。同一代码库带来了共享约定和契约的唯一事实来源,但不会引入共享部署。
拆分还导致可观测性碎片化。一个Worker能看到整个请求,而Worker链上的每个Worker只能看到自己那一小段。如果想观察一个在途请求,又不想依赖集中式日志,一种办法是使用特殊的请求标头,把诊断信息放到响应头里返回。这在网关层是有效的,但在服务绑定下游的功能Worker内部捕获的诊断信息不会被传播回来,比如请求进入图片优化Worker的信息就会丢失。端到端追踪一个请求,是单体免费自带的。
同一套架构,换到另一家CDN就不成立
这套架构是基于Cloudflare的方案。人们很容易想当然:只要CDN支持无服务器计算,就能直接迁移过去。但事实并非如此。在Akamai和Cloudflare上实现同一个图片优化功能,两者差异远大于“把代码放到边缘”这种简单设想。根源来自平台提供的是不同的底层原语。
在Cloudflare上,计算单元是Worker。入口点是单个fetch处理函数,端到端地处理请求:检查请求、做决策、通过绑定调用其他Worker、获取源站、重写响应。网关模式可以自然地在这里实现,因为一段代码持有整个请求并能组合其余部分。
Akamai的配置单元是property——一个根据请求属性匹配并应用行为的规则引擎。图片优化是其中的一种行为:一个托管产品,靠规则开启,而不是手写代码实现。计算(即Akamai EdgeWorker)是这条流水线里的一个插件,在指定生命周期事件触发执行,并不拥有整个请求。它不会调用Akamai的Image Manager,也不会转换图片。
在Cloudflare上,决策和执行在同一段代码里。在Akamai上,EdgeWorker把决策写入请求状态,property内部的一条规则读取该变量并控制是否启用Image Manager。这两部分不在同一个调用栈,也没有类似服务绑定的机制把自定义计算和托管转换连接起来。
这个差异会传导到数据平面。选择退出状态在两个平台上都放在键值存储里,但这两个存储的可达范围不同。Cloudflare Workers KV是全局的,写入一个键,它在任何地方都是可读的,网关不用关心地域。Akamai的EdgeKV命名空间是按区域配置的,在创建时选择,不存在跨所有区域的全局命名空间。所以Akamai的Worker里有一段Cloudflare完全不需要的代码,它在读取之前会先把请求的源站所在大洲映射到区域存储。
不过这个地域限制后来有所改善,Akamai新增了全局命名空间。平台一直在迭代。今年你为某个差异做兼容,明年这个差异可能就消失了。平台能力对等不是一个一劳永逸的状态,两边能力会来回变化。
哪种会变成负担,取决于你的目标。平台想要全球覆盖,那么自动全局的存储更简单,区域路由就是额外的开销。换个需求,数据驻留合规要求区域数据留在本区域,二者就反过来了。Akamai按区域隔离的命名空间正好满足合规,变成了优势;Cloudflare KV的设计是全局的,没有区域开关,要实现数据驻留,就得换别的原语,而不只是KV的一个设置。
既然代码无法直接复用,什么东西可以跨平台保留?是设计意图,以及经过实践验证的不变量。两个平台都限制边缘计算可使用的内存,所以在两个平台上,都在键值存储前放置了一个小的、有容量上限的本地缓存,定时清理保证数据新鲜。缓存结构、容量上限、过期窗口都保持一致,只是代码写法不同。这个约束来自边缘本身,不是来自厂商:内存上限加上热路径延迟敏感,不能每次请求都访问远端存储,因此两个平台都要实现同样的行为。
所以多CDN并不是“写一次,到处部署”,而是持续的适配成本:一个功能必须在两个CDN都正常运行才算完成。除了边缘本身带来的硬性约束,两种实现的其余部分都会走向分化。
图片优化:托管服务与原语的两条路
图片优化只是网关编排的众多功能之一,并不是平台本身的目标。两个CDN上,“自研还是直接用托管服务”的选择截然相反。
两个平台抽象层级不一样。在Akamai上,图片优化是一项托管服务。你配置一个策略,它自行协商每个请求,不需要逐请求运行代码。在Cloudflare上,它是一个更低层的原语:你在Worker内部按请求指定转换。Cloudflare当然也提供了自己的托管层级,但它进行了缓存优化,按文件扩展名匹配URL,并跳过任何不可公开缓存的内容。这个平台的图片流量既没有统一的扩展名,也不是统一公开的,所以这个开关并不适用。
没有谁更先进,只是抽象模型不同。托管服务接受配置,而原语接受代码。直接使用前者,在后者之上自行构建。Cloudflare文档也写得很清楚:使用底层原语时,自动格式协商要由调用方自己实现。
工作量更大,但换来的是策略写在代码里,不管CDN托管产品能力、缺陷、后续版本怎么变,策略行为保持一致。它也要求你必须清晰说明“协商图像”是什么意思,这包含三个独立的决策,需要依次作出。
第一个是安全。多租户平台上的一些图片是私有的,鉴权逻辑放在源站,边缘默认不会复现鉴权。缓存会让这件事变得危险。如果你优化了一张私有图片,图片会以URL作为键存入共享边缘缓存,下一次相同URL请求直接返回——这是边缘图片优化里最先要处理的问题。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
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.