Workers RPC 的边界已经从同语言调用扩展到 Python 与 JavaScript 之间。一个编码代理可以实现 Python Worker,另一个负责 JavaScript Worker;运行时不仅能让两端调用方法,还能传递活对象引用,而不必先设计 HTTP API、维护 Schema 或编写序列化代码。
这项变化的重点不只是“少写几个接口”,而是把跨 Worker 协作从数据传输问题,改造成对象能力调用问题。
从传 JSON 变成传能力
传统跨语言服务通常围绕消息构建边界:调用方把对象压成 JSON,服务端解析字段、执行业务逻辑,再把结果序列化回来。随着系统演进,团队还要处理版本兼容、字段缺失、日期与大整数格式,以及错误码映射。
Workers RPC 提供的是另一种模型。Python Worker 可以返回一个活对象的引用,JavaScript Worker 持有这个引用后,能够继续调用对象公开的方法。引用背后的对象仍运行在原 Worker 中,并没有被复制到调用方进程。
可以把两种模型简化为:
HTTP/JSON:
JavaScript -> serialize -> network -> parse -> Python
Workers RPC:
JavaScript -> remote object reference -> Python method
因此,开发者不再需要为内部调用重复定义路由、请求体和响应体。对象公开的方法本身构成调用界面,运行时负责跨语言调用和引用传递。
这并不意味着接口设计消失了。方法名称、参数含义、返回对象的生命周期和错误语义,仍然是调用契约,只是契约不再集中写在 OpenAPI 或 JSON Schema 中。
活对象引用适合哪些场景
活对象引用特别适合带状态或需要多步交互的工作流。例如,Python Worker 创建一个分析会话,JavaScript Worker 随后向同一个会话追加数据、读取进度并取得结果。使用普通 HTTP 时,每次请求通常都要携带 session_id,服务端再查找对应状态;使用对象引用时,调用方可以直接保存会话对象并调用它的方法。
典型场景包括:
- Python 承担模型推理、数据分析或科学计算,JavaScript 负责请求编排与 Web 响应。
- 一个 Worker 创建任务、游标或会话对象,另一个 Worker连续执行多步操作。
- 多个编码代理分别实现不同语言模块,通过对象接口完成集成。
- 内部服务希望减少 DTO、路由和序列化样板代码。
边界也需要看清。远程对象调用仍然会产生网络延迟和运行时开销,不应把它当成本地对象使用。例如,对一万个元素逐个调用远程 add(),通常不如一次调用 add_batch(items)。接口应保持粗粒度,并避免在高频循环中跨 Worker 往返。
一个跨语言会话示例
下面是一个可改造的最小项目,用来展示 Python Worker 返回会话对象、JavaScript Worker继续调用其方法的结构。由于来源摘要没有给出具体 SDK 版本和部署配置,示例采用 Workers RPC 的概念性接口;请按当前运行时提供的 Python 基类、RPC 目标类型和服务绑定语法调整导入名称。
目录可以这样组织:
workers-rpc-demo/
├── python-worker/
│ └── src.py
└── javascript-worker/
└── src.js
Python Worker 创建一个分析会话:
# python-worker/src.py
# 假设当前 Workers Python SDK 提供 WorkerEntrypoint 与 RpcTarget。
from workers import WorkerEntrypoint, RpcTarget
class AnalysisSession(RpcTarget):
def __init__(self, name: str):
self.name = name
self.values = []
async def add_batch(self, values: list[float]) -> int:
self.values.extend(values)
return len(self.values)
async def summary(self) -> dict:
count = len(self.values)
total = sum(self.values)
return {
"name": self.name,
"count": count,
"average": total / count if count else None,
}
class Default(WorkerEntrypoint):
async def create_session(self, name: str) -> AnalysisSession:
return AnalysisSession(name)
JavaScript Worker 通过服务绑定取得对象引用并继续调用:
// javascript-worker/src.js
export default {
async fetch(request, env) {
// PYTHON_WORKER 是绑定到 Python Worker 的服务名。
const session = await env.PYTHON_WORKER.create_session("latency-check");
await session.add_batch([18.2, 20.1, 17.8, 19.4]);
const result = await session.summary();
return Response.json(result);
},
};
预期响应为:
{
"name": "latency-check",
"count": 4,
"average": 18.875
}
部署时需要把 JavaScript Worker 的 PYTHON_WORKER 绑定指向 Python Worker。下面使用 YAML 表达与平台无关的配置意图;它是可改造的配置草案,不代表某个 CLI 的原生文件格式:
services:
python-worker:
language: python
entrypoint: python-worker/src.py
javascript-worker:
language: javascript
entrypoint: javascript-worker/src.js
bindings:
PYTHON_WORKER:
service: python-worker
实际接入时,应将这段配置映射到当前部署工具支持的服务绑定格式,并确认平台是否要求显式导出 RPC 入口类。
不写 Schema,不等于没有契约
Workers RPC 省去了显式序列化层,但跨语言类型仍需谨慎选择。字符串、数字、布尔值、列表和普通对象通常最容易在两种语言之间形成稳定约定。语言专属类型则可能带来歧义,例如 Python 的任意精度整数、datetime、生成器,以及 JavaScript 的 BigInt、Symbol 或带原型链的复杂实例。
项目中可以保留一组跨语言契约测试,而不必引入完整 Schema:
// contract-test.mjs
import assert from "node:assert/strict";
export async function verifyAnalysisService(service) {
const session = await service.create_session("contract-test");
const count = await session.add_batch([10, 20, 30]);
const result = await session.summary();
assert.equal(count, 3);
assert.deepEqual(result, {
name: "contract-test",
count: 3,
average: 20,
});
}
还应明确远程对象何时失效、是否能跨请求保存、并发调用如何处理,以及异常会以什么形式传播。对于支付、审计或外部合作方接口,显式版本化的 HTTP API 和 Schema 往往仍然更合适,因为它们更容易独立验证、记录和长期兼容。
落地时检查这五件事
引入跨语言 Workers RPC 时,可以从一个内部、低风险的调用链开始,并逐项检查:
- 方法是否足够粗粒度,避免循环中的大量远程往返。
- 参数与返回值是否使用两种语言都能稳定表达的类型。
- 活对象引用的生命周期、并发行为和失效方式是否清楚。
- 超时、异常、重试是否会造成重复写入或状态不一致。
- 是否有跨语言契约测试覆盖方法签名和关键返回结构。
Workers RPC 让 Python 与 JavaScript Worker 可以按各自擅长的领域分工,同时保留接近对象调用的编程体验。它减少了内部接口的机械代码,但没有消除分布式系统的成本。把调用设计得粗粒度、明确对象生命周期,并用契约测试守住边界,才能真正获得跨语言 RPC 带来的开发效率。