Workers 的更新日志 8 月 3 日加了一条:Python Worker 和 JavaScript Worker 现在可以互相通过 RPC 调用方法。走 Service bindings,不需要额外依赖,不需要定义 schema,不需要写序列化代码。
我第一反应是”这不就是又一个内部通信糖”。翻了一遍那条 changelog 之后改了看法,值得单独说一说的地方在于它没有走 HTTP。
之前你只能这么写
一个 Python Worker 想调 TypeScript Worker 里的函数,过去的路径基本固定:TypeScript 那边挂一个 fetch handler,约定一个路径,Python 这边 await env.SVC.fetch(...),两头各写一遍 JSON 编解码。参数是 dict 就 json.dumps,回来的东西是 bytes 就再 json.loads。
这套东西的问题不在性能。Service bindings 本来就在同一个进程边界内,不出网卡。问题在于你手写了一层协议,而这层协议只有你自己知道。字段名拼错,运行时才炸;返回值多了个 key,另一边默默忽略;异常没法传,只能靠状态码和一个 {"error": "..."} 约定。
changelog 里那句话我看了两遍:“Cross-language RPC calls behave like ordinary function calls. Exceptions propagate to the call site.”
异常能传到调用点。这句是整条更新里我最在意的。
类型转换发生在哪一层
素材给的 TypeScript 侧写法是这样:
import { WorkerEntrypoint } from "cloudflare:workers";
export class RpcService extends WorkerEntrypoint {
// ...
}
WorkerEntrypoint 这个基类不是新东西,Workers RPC 一直是这么定义服务端的。新的部分在 Python 侧怎么把调用发过去、结果怎么变回 Python 对象。
changelog 说的是:可以传 structured cloneable 类型作为参数和返回值,Pyodide 的 FFI 负责在两种语言之间自动转换类型。
这两句话得连起来读。structured cloneable 是 JavaScript 那边的概念,划定了哪些值能跨隔离边界搬运。Pyodide FFI 是 Python 那边的机制,Python Workers 本来就跑在 Pyodide 上,FFI 是它和 JS 之间原有的桥。所以这个功能的实现路径大致是把 Workers RPC 已有的序列化语义接到 Pyodide 已有的类型映射上,两边都不是为这次特意造的。
我不喜欢这里的一点:文档没说清楚边界在哪。structured clone 不支持函数,不支持类实例的原型链,Python 那边的 dict 和 JS 的 plain object 能对上,但 Python 的 tuple 呢,set 呢,datetime 呢。Pyodide 自己的类型转换表是有的,Workers RPC 的可传输类型表也是有的,交集是什么,得自己试。我目前没有一个可靠的答案,只能说别把复杂对象当参数扔过去。
为什么这比看起来重要
Python Workers 一直有个尴尬处境。生态里最想用 Python 的场景是数据处理和调模型,但 Workers 平台上大量的绑定、helper、社区代码都是 TypeScript 写的。之前的选择是要么把整个 Worker 用 TypeScript 重写,要么在两个 Worker 之间搭一层 HTTP 桥。
现在多了第三条:Python 负责它擅长的那段,TypeScript 那边的东西直接当函数调。拆分点可以按语言优势划,不用再按”哪边通信成本低”划。
顺带说一句,8 月 3 日前后 Workers 平台上的通信相关更新不止这一条。同期还有 Workers 原生支持 gRPC,让开发者在边缘直接部署 RPC 服务,不用再自己维护专用服务器或者代理层。两件事的方向是一致的:把过去需要外部组件承担的通信职责收进运行时。区别在于 gRPC 面向的是跨系统的对外接口,跨语言 RPC 面向的是同一个项目内部的模块划分。
调试怎么办
跨语言调用最怕的是出错之后不知道错在哪一侧。异常能传到调用点解决了一半问题,另一半是链路可见性。
这里正好接得上 8 月 4 日的另一条更新:wrangler dev 和 vite dev 现在会自动为本地 Worker 调用采集 OpenTelemetry trace。原话是不需要装 SDK,不需要开启 tracing,不需要配置,甚至不需要在 prompt 里提 observability。Cloudflare 的工具链检测到 agent session 时会把 agent 指向 Local Explorer API,一个本地调试用的查询接口。
博客里给的示例 prompt 短得有点挑衅:
POST /api/orders is returning 500. Find the cause, fix it, and verify the fix locally.
那篇文章提到这是建立在多年本地开发投入上的,从最早的 Miniflare 到 Wrangler 3 把 local mode 设为默认。
我把这两条放一起看是因为它们在解决同一个问题的两面。RPC 让跨语言调用像普通函数调用,本地 trace 让这个”普通函数调用”在出问题时还能被看见。如果只有前者,一个 Python 侧的 await 卡住了,你得靠 print 定位是没发出去还是没收到。
我还没验证的部分
写这篇的时候我没有跑通完整的双向调用。几个具体的疑问先记下来:
Python 侧调用 TypeScript 方法时,那个 await 的等待成本有多少。Service bindings 之间的调用理论上不出隔离边界,但 Pyodide FFI 的类型转换是有开销的,一个大 dict 转过去要多久,没有数据。
异常传播到 Python 侧之后是什么类型。是包装成一个统一的 RPC 异常,还是尽量映射到 Python 内建异常,changelog 没写。
反方向的调用,也就是 TypeScript Worker 调 Python Worker 的方法,changelog 标题写的是”can now call each other”,示例给的是 Python 调 TypeScript。另一个方向应该也支持,但示例代码里没有。
顺便提醒一句,8 月 3 日当天 Workers builds 出过一次故障,持续约 1.9 小时,多个客户受影响,Cloudflare 修复后转入监控。如果你那两天构建失败过,不一定是你的代码问题。
changelog 页面地址是 developers.cloudflare.com/changelog/post/2026-08-03-python-javascript-rpc/。