跳至正文
Workers — Cap'n Web RPC:远程对象、Promise pipelining 与使用方式

Cap'n Web RPC:远程对象、Promise pipelining 与使用方式

Cloudflare 原文:Cap’n Web — a new RPC system for browsers and web servers

AI 参与说明(Agent:Codex):本文由 Codex 基于 Cloudflare 发布文章、官方文档与固定版本源码辅助整理,并运行最小示例。整理日期:2026-10-21。源码分析固定为 766655329fe4fe2dada67c8b4e82e3a81e38b194;示例使用 npm capnweb@0.12.0,两者是不同快照。执行入口为 Codex Desktop;完整模型标识与 reasoning effort 未取得任务级运行记录。

DeepWiki:cloudflare/capnweb

场景分析补充(2026-10-22,Agent:Codex):补充交互式工作区、插件、Agent 执行器、实时协作和依赖调用的工程取舍。场景与采用优先级属于基于文中协议契约作出的工程判断,不代表官方产品保证;原有示例与源码分析快照保持不变。执行入口为 Codex Desktop;完整模型标识与 reasoning effort 未取得任务级运行记录。

资料修订(2026-11-01,Agent:Cursor):按 7666553 补上 Set 按值传输的限制,并按 143d13d 说明 structured clone 传输只发送字节视图自己的区间。未重跑 Counter 示例。模型 grok-4.7-high-fast,提供方 xAI,执行入口 Cursor Cloud。reasoning effort 未作为独立字段取得运行记录。

Cap’n Web 让浏览器与服务器通过 JavaScript 对象交互:方法可以返回另一个带方法的远程对象,客户端也可以把回调传给服务器。它最值得关注的能力是 Promise pipelining,让后一次调用直接引用前一次尚未返回的结果,减少连续操作中的网络等待。

这套模型适合前后端均使用 JavaScript / TypeScript、需要持续交互或细粒度授权的应用。它是 MIT 开源的协议和库,可以运行在浏览器、Cloudflare Workers、Node.js、Bun、Deno 等环境中,不要求部署在 Cloudflare。原文发布于 2025-09-22;以下接口与机制同时对照当前固定源码,不沿用发布时的包体积或实验状态描述。官方 README

英文术语中文名称简要解释
RPC远程过程调用通过调用方法来请求另一个进程执行操作
object-capability对象能力持有某个对象的引用,就获得调用该对象所公开能力的权限
RpcTarget保留 API 名称标记可以按引用传给远端、供对方调用的类
RpcStub远程代理客户端持有的代理对象,调用会被发送给真正持有对象的一方
RpcPromise远程结果承诺可以等待,也可以继续调用其未来结果的特殊代理
Promise pipeliningPromise 流水线先描述依赖调用,让远端使用中间结果继续执行
HTTP batchHTTP 批处理把一组 RPC 消息放在一次 HTTP 请求与响应中完成

从返回数据到返回远程对象

A Cap’n Web call can return a live object reference, and later calls execute where that object lives. RpcTarget 与公共 API

假设服务器提供 getCounter(10),返回一个初始值为 10 的 Counter。客户端拿到的不是这个对象的完整副本,而是能调用它的 RpcStub。调用 counter.add(5) 时,服务器上的对象更新为 15;调用 counter.read() 时,服务器返回这个数值。

普通对象、数组和 Set 主要按值传输。Set 在编码任何一个元素之前,会先检查全部直接元素:元素不能直接是 Promise 或 Blob。需要保留方法与身份的类继承 RpcTarget,函数则可以按引用传递。函数也不需要传输源码:服务器收到客户端回调的 RpcStub,调用时再通过连接回到客户端执行。因此,在 WebSocket 或 MessagePort 上,双方都可以发起调用。Set 的编码、值与引用的编码

143d13d 收紧了 structured clone 传输上的字节视图。structuredClonable 和 jsonCompatibleWithBytes 会把字节交给传输层,而 structured clone 复制的是整个 backing buffer。视图没有盖住整个 buffer 时,发送侧先复制 byteOffset 起、长度为 byteLength 的那一段,再交给传输。否则 MessagePort 对端可能读到 subarray 旁边的字节,Node 里一个小 Buffer 还可能带出它所在的共享池。接收侧再复制挡不住对端读取原始消息。走 base64 的编码路径本来就只包含这一段。上文的 Counter 示例没有覆盖 Set 或这次复制;其余源码分析仍钉在 7666553。只发送视图自己的字节

适合哪些场景:从业务交互判断

我最看好的是这样一类应用:用户进入一个工作区后,会持续操作一组有身份、有权限、具有后续行为的远程对象。在线 IDE、设计工具、插件平台和 Agent 工作台都可能符合这一特征。其次是依赖关系已经确定、却因为逐次等待而变慢的调用链。

这里有两个独立收益。对象引用让接口接近业务关系,也可以承载受限权限;Promise pipelining 减少能提前描述的依赖链中的网络等待。一个插件通过同页 MessagePort 通信时,主要获得前一个收益;移动网络上的 HTTP batch 则可能主要获得后一个收益。选型时应分别判断,不要求每个场景同时用到所有能力。

交互式工作区:反复操作同一组资源

例如在线 IDE 中的 Workspace、Document、Terminal,或设计工具中的 Project、Canvas、Layer。用户打开资源后,会继续修改、保存、监听变化或执行命令,接口天然具有层次和生命周期。

下面是接口设计草图,不属于后面的 Counter 服务;权限、持久化和变更格式由应用实现:

js
const workspace = api.openWorkspace(workspaceId);
const document = workspace.openDocument(documentId);
const revision = await document.applyChanges(changes);

这里的价值是,workspace 已经代表某个访问范围,document 又把后续操作收敛到一份文档。服务端在发放对象时核验权限,并通过所暴露的方法限制行为;客户端后续不需要每次都重新描述完整资源路径。依赖图能提前确定时,这几次调用也能使用 Promise pipelining。对象与依赖调用

适合持续使用的对象接口,不等于把所有业务状态放进连接。文档内容与修订号仍应保存到持久存储;Document 的 RpcStub 是访问它的一种方式。连接断开后,应能凭稳定的工作区与文档 ID 重新打开。权限撤销也需要对象内部检查或其他明确机制,不能假定取得过一次引用就永远有效。session 与重建

如果一个后台只是“查询用户列表、提交一张表单”,而用户对象没有后续行为,普通数据接口通常更直接。我不会仅为了少写几个 fetch(),让简单 CRUD 承担远程引用、释放和断线恢复的成本。

插件、iframe 与 Web Worker:按对象交付有限能力

假设图片编辑器允许第三方插件运行。一个色彩分析插件只需要读取当前图片,一个滤镜插件可以提交当前图片的修改;它们都不需要取得整个账户、所有文件和宿主的完整 API。

Cap’n Web 可以让宿主给插件一个较窄的对象,例如只提供 readPixels(),或额外提供 applyFilter()。这比让所有插件访问同一个全能入口更容易看清权限范围。调用返回值、错误和回调也沿用统一 RPC 模型,减少手写 postMessage 消息分派的工作。MessagePort 接入

我认为这类场景中,接口与权限表达的收益往往比延迟收益更重要。同一浏览器中的消息传递不应套用跨地域网络的性能估算。

object-capability 只能约束经这些对象提供的能力,插件已有的其他权限仍需浏览器隔离策略管理。宿主需要先建立适当的 iframe sandbox、origin 或 Worker 边界,再确认 MessagePort 来自预期发送方。官方要求新建 MessageChannel 并转交其中一个 port,不能直接把 Window 当作 RPC port。把一个全能对象交给插件,也不会自动获得最小权限。

Agent 执行器:受限工具对象与可组合操作

这里值得采用的通常是 Agent 的工具执行层。例如,一次任务只允许读取某个项目、修改指定输出目录,或操作某个浏览器会话。可信执行器可以持有相应的受限对象,模型提出的工具请求由执行器转成 RPC,而不必把全局凭据交给每段生成代码。

具体收益是:资源边界可以由对象承载,工具返回的资源还能继续操作,进度可以通过回调报告。前提是底层实现真正检查目录、资源归属和可执行操作;预算、审批、审计与撤销也需要应用维护。Cap’n Web 本身不提供代码隔离、Agent planner 或模型策略。object-capability 的授权模式

还需要区分两种调用链:

  • 执行器已知后续操作:例如创建一个临时工作区,再在该工作区运行预先确定的检查;在任务句柄和依赖能提前表达时,可以使用 Promise pipelining。
  • 模型必须先阅读结果:例如先搜索代码,模型判断应修改哪个文件,再决定下一次工具调用。后续决定尚未产生,这段等待仍然存在。

因此,我更看好 Cap’n Web 在“Agent 工作台内部的工具与资源连接”中的价值。把一次普通聊天接口换成 Cap’n Web,不会直接加快模型推理,也不会自动降低多轮工具规划的成本。真实的多层能力组合可以继续阅读 Cloudflare OS 分析。

实时协作:对象会话与双向通知

共享画布、协作文档、房间式应用常有两类交互:客户端向某个资源提交操作,服务器把新状态或其他用户的活动通知客户端。返回 Room 或 Document 对象,再接收客户端的回调对象,可以把两类交互放在同一套 API 中,省去另写一套独立的消息分派协议。

但协作产品还必须回答“两个用户同时修改时,结果怎样确定”。Cap’n Web 不提供 CRDT、OT 或冲突合并;这些规则属于文档与协作模型。客户端回调也不构成可靠事件日志:断线期间发生的变化,需要应用用事件 ID、历史存储和重新订阅来补齐。官方可恢复订阅说明

我的采用边界会放在文档操作、会话管理和通知 API。高频光标、逐帧画面或大量细碎事件仍需按流量特征设计合并、节流和发送速率;一次方法调用写起来简单,不意味着适合为每个像素或每一帧都发一次 RPC。

在 Cloudflare 上,可以将同一房间或文档的连接路由到对应 Durable Object,由应用在那里维护协调与持久数据,Cap’n Web 负责对外的调用关系。两层的职责应分别设计,不把 RPC session 当成 Durable Object 存储。Workers 与 Durable Objects 接入

高延迟下的依赖调用:提前表达整条操作链

移动网络和跨地域访问中,常见损耗是客户端一直在等中间结果。假设一条链有三次依赖调用,每次网络往返为 100 ms,忽略服务器计算、传输大小与连接建立,逐次等待约花 300 ms;若能合为一次往返,网络等待约为 100 ms。这只是解释机制的估算,不能作为实际性能指标。

HTTP batch 很适合这种一次性操作:不用维持长期连接,仍能在同一请求内使用之前调用的结果。但完整依赖必须能在第一次等待前表达,服务器的工作量和数据库查询次数也不会因此自动减少。HTTP batch 契约

比较时还应加入一个现实的基准:服务端已有的聚合接口。如果一个 createAndConfigureProject() 已经在一次请求内完成全部工作,Cap’n Web 不会再把一次往返减少成零。它可能提供的额外价值是让客户端组合已有方法,而不用为每种固定组合都新建一个聚合入口;是否值得,要与接口治理和限流成本一起衡量。

同理,如果主要耗时来自数据库扫描、模型推理或大文件处理,或客户端与服务器的 RTT 已经很低,应先处理真正的瓶颈。网络往返与数据库 N+1

持久任务:提供操作入口与进度界面

视频转码、代码构建或数据导入可以用 Cap’n Web 提供启动、查询、取消及当前进度接口。任务本身则需要独立保存状态、处理重试,并能通过稳定的 job ID 重新找到。

An RPC session is temporary; a durable job must remain recoverable after that session disappears. 如果用户关闭页面后任务仍应继续,任务生命周期就不能只依赖当前 WebSocket 或某个返回的 RpcStub。重连后应重新取得任务对象,再从已处理的进度位置恢复观察。断线后的设计要求

我的选择会是:用它组织交互界面与任务控制,持久执行交给已有的工作流或任务系统。不能仅凭回调就承诺任务可靠执行、消息不丢或失败后自动恢复。

采用优先级与不优先采用的情况

场景我的采用倾向收益成立的前提与主要成本
在线 IDE、设计工具、交互式工作区优先评估资源具有后续行为;能管理对象权限、释放与重连
iframe 插件、Web Worker 模块优先评估有明确的上下文隔离,能设计较窄的对象接口
Agent 的工具执行与资源访问层值得评估执行器可信、资源边界真实有效;已知调用链才适合提前组合
实时协作的操作与通知接口适合做通信层一致性、断线补发和高频事件处理由应用实现
高 RTT 下的一次性依赖链有条件采用 HTTP batch可提前描述依赖;与现有聚合接口比较收益
简单 CRUD、单向查询和表单提交通常沿用现有接口远程对象和双向调用收益有限,避免引入额外生命周期成本
面向不受控客户端的公共多语言 API通常不作为默认入口先满足公开契约、SDK、版本兼容与 HTTP 缓存等要求
持久工作流、可靠事件分发仅用于交互与控制接口执行状态、幂等、重试及事件历史需要其他机制

我的实际接入顺序会从一个边界清楚的模块开始,例如文档编辑会话或插件桥接,保留已有公开数据接口。验证时重点看:是否减少了手写消息分派与重复资源参数,是否真的减少了端到端等待,以及断线后能否用稳定 ID 重建对象。只看到代码行数减少,或只测本机 Counter 的返回速度,还不足以判断整个应用值得迁移。

公共 API 的取舍也不意味着协议从原理上排斥其他语言。其他语言可以实现兼容 peer;当前官方库的主要使用环境是 JS/TS,需要自己承担的客户端与兼容性工作仍应计入成本。无需独立 IDL,也不免除旧客户端兼容与滚动部署的责任。多语言边界、API 演进

一个可以运行的例子

下面使用 Node.js 24 和 capnweb@0.12.0,只监听本机。getCounter() 返回远程对象,add() 返回同一个对象,再由 read() 取值。示例不涉及认证或持久化。

在一个空的实验目录安装依赖:

sh
npm init -y
npm install --save-exact capnweb@0.12.0

保存为 server.mjs:

js
import { createServer } from 'node:http';
import { RpcTarget, nodeHttpBatchRpcResponse } from 'capnweb';

class Counter extends RpcTarget {
  #value;

  constructor(start) {
    super();
    this.#value = start;
  }

  add(delta) {
    if (typeof delta !== 'number' || !Number.isFinite(delta)) {
      throw new TypeError('delta must be a finite number');
    }
    this.#value += delta;
    return this;
  }

  read() {
    return this.#value;
  }
}

class Api extends RpcTarget {
  getCounter(start) {
    if (typeof start !== 'number' || !Number.isFinite(start)) {
      throw new TypeError('start must be a finite number');
    }
    return new Counter(start);
  }
}

let posts = 0;
const server = createServer(async (request, response) => {
  if (request.url !== '/api') {
    response.writeHead(404).end();
    return;
  }
  if (request.method === 'POST') console.log(`POST /api #${++posts}`);
  try {
    await nodeHttpBatchRpcResponse(request, response, new Api());
  } catch (error) {
    console.error(error);
    response.writeHead(500).end();
  }
});

server.listen(8787, '127.0.0.1', () => {
  console.log('RPC: http://127.0.0.1:8787/api');
});

保存为 client.mjs:

js
import { newHttpBatchRpcSession } from 'capnweb';

const api = newHttpBatchRpcSession('http://127.0.0.1:8787/api');
const counter = api.getCounter(10);
const updated = counter.add(5);
const result = await updated.read();
console.log(result);

先在一个终端执行 node server.mjs,再在另一个终端执行 node client.mjs。客户端输出 15,服务器新增一行 POST /api #1。三个有依赖的 RPC 只使用一次 HTTP 请求与响应。本文在 Node.js 24.19.0 上运行得到这一结果。Node HTTP 适配入口

counter 和 updated 都是 RpcPromise。客户端还没有收到真实对象,就已经能描述后续操作;服务器会先获得 Counter,再执行 add(5),最后执行 read()。

Promise pipelining 怎样节省往返

Promise pipelining removes intermediate network waits while preserving dependencies between calls. 官方 Pipelining Tour

如果通过持续连接逐次 await getCounter()、await add()、await read(),客户端需要三次等待回复。上面的写法提前描述完整依赖关系,HTTP batch 将这些消息一起发送。连接建立和服务器计算仍需要时间,减少的是中途把结果带回客户端、再发下一次请求的等待。

sequenceDiagram
  participant C as Client
  participant S as Server
  Note over C,S: 下列四条请求消息放在同一个 HTTP POST 中
  C->>S: push 1:getCounter(10)
  C->>S: push 2:结果 1 的 add(5)
  C->>S: push 3:结果 2 的 read()
  C->>S: pull 3:请求取回最终结果
  Note over S: 按依赖取得 Counter、加 5、读取数值
  S-->>C: resolve 3:15

在示例的实际 HTTP 请求体中,可以看到四行 JSON:

json
["push",["pipeline",0,["getCounter"],[10]]]
["push",["pipeline",1,["add"],[5]]]
["push",["pipeline",2,["read"],[]]]
["pull",3]

这是按行传输的四条 JSON 消息,不是一个 JSON 数组。ID 0 代表初始 API,随后三次调用的结果分别取得 ID 1、2、3。后续消息引用这些 ID,就不必先收到对象;最终响应为 ["resolve",3,15]。

需要区分“发起调用”和“取回结果”。Calling a method sends or buffers a push; awaiting its RpcPromise requests the result with pull. WebSocket 的 push 可以立即发送;HTTP batch 的 transport 先收集消息,再在下一宏任务发出 POST。不能把两种模式都理解为“只有 await 才联网”。sendCall 与 sendPull、BatchClientTransport

如果客户端必须先读出某个结果、用本地 if 判断,再决定调用哪个方法,这一步仍然要等待。Promise pipelining 也不提供数据库事务的原子性:链中某个后续操作失败,不表示此前的业务写入会回滚。

架构与源码入口

各 transport 共用同一套对象、结果和消息模型。可以从下表按问题定位,而不必先通读整个仓库。

模块职责对应能力与入口
src/index.ts对外 API 与运行时适配RpcTarget、RpcStub、RpcPromise 与 Workers 入口
src/core.ts代理属性路径、方法调用与应用值交付PROXY_HANDLERS、RpcPromise
src/rpc.tssession、引用表、消息收发与错误传播RpcSessionImpl
src/serialize.ts值编码与调用表达式求值Devaluator、Evaluator
src/batch.ts、src/websocket.ts、src/messageport.ts通过不同连接传输消息HTTP batch、WebSocket、MessagePort
src/map.ts记录并重放逐元素 RPC 操作sendMap、applyMap

支撑这些模块的核心数据包括:

数据保存什么在例子中的作用
RpcStub 与 RpcPromise指向调用目标的 hook,以及属性路径或未来结果让 counter.add 表示一次远程方法调用
RpcPayload参数或返回值,以及其中引用的所有权传递 10、5、Counter 引用与最终 15
import table本地持有的远端引用、等待结果与引用计数记录客户端等待的结果 ID 3
export table向对端暴露的本地目标、结果与引用计数保存初始 Api、Counter 和后续结果

沿 getCounter(10) 到 15 的真实路径看:PROXY_HANDLERS.apply() 收到方法调用,封装 RpcPayload;RpcImportHook.call() 进入 sendCall(),编码 pipeline 并发出 push。服务端 readLoop() 收到消息后,用 Evaluator 求值并把结果放入 export table。下一条消息中的 ID 引用由此找到前一结果,最终通过 RpcPayload.deliverCall() 在参数准备好后调用应用方法。代理与调用、readLoop、deliverCall

最后一次 await 进入 RpcPromise 的 then() / pullPromise() 路径,要求远端传回结果。服务端收到 pull 后等待对应值,发送 resolve 或 reject;客户端按 import ID 找回等待项,交给 JavaScript 调用方。因此,中间对象可以始终留在服务器,客户端只接收最后的数值。pullPromise、结果解析与错误传播

object-capability 怎样表达权限

Counter 示例展示了对象引用,但没有权限设计。实际应用可以只公开 authenticate(token),认证成功后返回绑定某个用户的 AuthenticatedApi,再由它返回该用户可编辑的文档对象。

Holding an object reference grants access to the operations that object exposes. 客户端不能仅凭自己构造一段对象数据就取得某个会话的有效引用;服务端必须在允许访问时把 capability 交给它。不同用户取得不同对象,也就可以具有不同的操作范围。官方安全说明

这不代表所有授权检查都能取消。对象内部仍需维护业务约束;权限撤销、过期、资源归属与是否允许转授,都要按应用场景设计。官方建议通过 RPC 方法在连接内提交 token 并取得授权对象,不把整个连接的权限隐含在调用顺序里。

TypeScript 类型在运行时会被擦除。示例中的 typeof 和 Number.isFinite() 才真正拒绝错误参数;实际 API 可以使用既有 schema 校验库,或选择官方配套的 capnweb-validate。后者需要单独接入,不能把“无需独立 schema 编译”理解成“输入自动可信”。另外,TypeScript 的 private 不会在运行时隐藏方法;确需私有成员时使用 JavaScript # 语法。Types are not validation

列表的 map 做了什么

假设另一个 API 提供 listUserIds() 和 getUserProfile(id),可以这样描述逐元素调用;这是接口示意片段,不属于前面的 Counter 服务:

js
const profiles = await api.listUserIds().map(id => api.getUserProfile(id));

The map callback runs once on the calling side to record RPC operations; the peer replays those operations for each element. 客户端先用占位值运行同步回调,记录“取得 ID 后调用 getUserProfile”的操作,再把记录交给远端逐项执行。map 的机制与限制

因此,不能把它当成任意 JavaScript 的远程执行环境。回调不能 await,也不能依靠尚未取得的 ID 做本地条件判断或字符串拼接;普通本地副作用只会在记录时发生一次。回调捕获的 RpcStub 会交给对端,也必须在相应信任边界内使用。

减少客户端网络往返也不会自动合并数据库查询。如果每个 getUserProfile() 都单独查数据库,服务器仍然可能执行 N+1 次查询;需要合并读取时,应提供接受一组 ID 的方法并在业务层批量查询。官方 N+1 讨论

HTTP batch、WebSocket 与 Workers 接入

方式session 持续时间适合的交互
HTTP batch一次 HTTP 请求与响应一次性读取、提交及其中的依赖调用
WebSocket对应连接的生命周期持续交互、服务器调用客户端回调
MessagePort对应 port 的生命周期页面与 iframe、Web Worker 之间的对象调用

HTTP batch 收到结果后就结束,不能继续使用从该 batch 获得的对象引用。WebSocket 断开后,旧 RpcStub 也会失效;应用需要建立新 session、重新认证并取得新的引用。session 的引用表不是持久化数据库;长任务进度、断线续传位置与重复提交的处理需要单独设计。持续 session 中不再使用的引用也应按 disposal 规则释放。Sessions & Reconnection

本文同时实测了两个边界:Counter batch 完成后再次调用旧引用会被拒绝;新 batch 传入字符串 getCounter('10'),最终等待操作会收到服务端的 start must be a finite number 错误。

Cloudflare Workers 可以复用同样的业务类。下面是适配片段,需要把前面的 Counter、Api 类放在同一模块中,并将 Node HTTP server 部分替换为:

js
import { newWorkersRpcResponse } from 'capnweb';

export default {
  fetch(request) {
    if (new URL(request.url).pathname !== '/api') {
      return new Response('Not found', { status: 404 });
    }
    return newWorkersRpcResponse(request, new Api());
  },
};

newWorkersRpcResponse() 可以处理 HTTP batch 和 WebSocket upgrade;浏览器长期连接对应 newWebSocketRpcSession('wss://example.com/api')。这部分对照官方适配入口,实际运行结果来自前面的 Node HTTP 示例。Workers 入口

Cap’n Web 与原生 Workers RPC 属于不同接入方式:前者提供浏览器等环境可用的库与线协议,后者由 Workers 平台提供 Service bindings 等运行时接口。两者可以配合使用,但安装 capnweb 不会自动获得某个 Worker binding 的权限。

与其他方案怎样取舍

方案主要表达方式选型时值得比较的差异
REST资源、URL 与 HTTP 方法HTTP 缓存与通用客户端工具有成熟支持;Cap’n Web 更直接表达对象方法及依赖操作
tRPCTypeScript 类型约束的过程调用同样无需独立 IDL;Cap’n Web 的重点是远程对象、函数引用与依赖调用的 Promise pipelining
JSON-RPC方法名与数据参数通常不包含对象引用及其生命周期、对尚未返回结果的引用机制
GraphQL查询语言描述的数据图Cap’n Web 用对象调用构造依赖关系,但没有自动数据库批量加载或查询成本分析
Cap’n Protoschema、代码生成与二进制协议适合多语言场景;Cap’n Web 更贴近 JavaScript,两者线协议不兼容

Batching 合并的是参数已经确定的独立调用;Promise pipelining 还能表示后一次调用依赖前一次结果。这是比较 tRPC 与 Cap’n Web 时更有用的区分,不能仅根据“都支持批处理”判断能力相同。官方方案比较

如果应用本来就有会话对象、文档编辑对象、客户端回调,而且前后端主要使用 JS/TS,Cap’n Web 值得做小规模接入验证。如果主要目标是公开多语言 API、标准 HTTP 缓存或声明式数据查询,应先评估对应方案的客户端生态和运维方式。

对外服务还需要按业务操作限制开销。一个 HTTP 请求可以包含很多调用,嵌套 .map() 也可能放大工作量;仅统计请求数的网关限制不等于限制了昂贵操作。应在应用层限制批量规模、操作次数或业务配额,并按 transport 设置载荷上限。应用限流与消息大小

关联阅读

一手资料

博客助手

正在打开博客助手…