编译运行云工适配
把公共[编译运行接口](../../../豫言操作系统接口/编译运行/规范.汉语.md)映射到 Cloudflare Worker:用豫言写在云工宿主的句柄桥之上(编译。豫),不增加任何宿主原语,也不改 `宿主.mjs`;真正运行豫言编译器的是一个新的宿主 JavaScript 模块 [宿主/云工/编译运行.mjs](../../宿主/云工/编译运行.mjs)(“在操作系统里的 JS”),由平台外壳放进 Worker 的 env。
结构
- 运行器(宿主 JS):
宿主/云工/编译运行.mjs导出创建编译运行器工厂({binaryen, 编译模块, 桥模块, 取标准库资料?}),返回函数(资产绑定) → 运行器。运行器复用浏览器编译器宿主(宿主/浏览器/编译器/宿主.mjs的内存文件系统、执行模块与组装.mjs的创建组装器,与本模块同处操作系统目录,相对位置不变)。它不解析 HTTP、不决定响应形状,只做:校验请求(与规范同一套规则,防应用出错)→ 排队 → 每次运行新建内存文件系统(预置标准库.json.gz的资料,进程内缓存“加载承诺”)→ 写/包上下文与/用户程序/*→ 用执行模块运行编译器 Wasm(Binaryen 组装回调)→ 把阶段与输出排成事件队列 → 成功时读/程序.wasm、算 SHA-256。方法:启动、读事件、取产物、取产物Base64、结束(另有诊断用的运行数)。 - 适配(豫言):
编译。豫经云工绑定句柄 COMPILE <绑定名>取得运行器对象句柄(许可 `COMPILE` 列表授权,绑定名由应用传入,与 D1、R2 等绑定同一套机制),再用云工调用方法安全调它的方法:宿主方法的失败(含 Promise 拒绝)转成可捕获的豫言异常,消息前缀“编译运行:”;产物字节以Uint8Array句柄返回,经云工句柄取字节读出后立即释放;每次调用后释放运行器句柄。适配自己校验编译器名(1 至 128 字节、无控制字符)、运行号、请求大小(≤ 2097152 字节)与等待毫秒(1 至 60000)。 - 运行号只在启动它的事件内有效:适配返回的运行号是“事件标记:宿主运行号”,事件标记是每个 Wasm 实例(每个事件)生成一次的随机前缀(同 网页事件流 的流柄规则);别的事件里用它抛出“运行号不属于当前事件”。这也是宿主平台的硬约束:Worker 不允许一个请求启动的异步工作在别的请求里继续(本适配的一致性验证最初把接口五个函数各做成一个 HTTP 端点,编译因此永不启动、
读取编译运行事件一直返回状态 1——这就是该规则的来历)。 - 外壳接入:应用许可
{"COMPILE":["COMPILER_RUNTIME"]};外壳在调用应用之前把运行器放进env:
import 应用 from './<应用产物>/入口.mjs';
import binaryen from './组装器.mjs'; // Binaryen 第三方胶水(它自己静态导入组装器.wasm)
import 编译模块 from './资源/编译器.wasm';
import 桥模块 from './资源/值桥接.wasm';
import {创建编译运行器工厂} from '<操作系统目录>/宿主/云工/编译运行.mjs';
const 造运行器 = 创建编译运行器工厂({binaryen, 编译模块, 桥模块});
export default {fetch: (请求, 环境, 上下文) => 应用.fetch(请求, {...环境, COMPILER_RUNTIME: 造运行器(环境.ASSETS)}, 上下文)};
编译器 Wasm 与 Binaryen 的 Wasm 必须由外壳静态导入(Workers 不允许运行时从字节编译 Wasm),所以它们留在平台外壳一侧;ASSETS 绑定提供 标准库.json.gz(约 2.6 MiB,解压 JSON 约 57 MiB,不在豫言里解析)。云仓的用法见 应用/云端编译服务/编译外壳.mjs。
限额(运行器强制,规范一致)
请求 ≤ 2 MiB、文件 1 至 256 个、路径规则、内容合计 ≤ 262144 字节、产物 ≤ 8 MiB、事件文字合计 ≤ 16 MiB(超出的过程事件被丢弃,终止事件不丢)、编译器输出 ≤ 2 MiB 与编译期限 90 秒(由 执行模块 强制)、未终止的运行 ≤ 8 个、已终止而等待读取的运行 ≤ 32 个(丢弃最旧的)、运行存活 ≤ 5 分钟、等待毫秒 1 至 60000。编译阶段串行(同一时刻只有一份 57 MiB 的编译文件系统副本),FS 在编译结束后立即释放,只留产物字节。
已验证
一致性验证在 `一致性验证/`:真实豫言 Wasm(试验应用)+ 真实运行器 + 真实编译器与标准库,在 Node 进程内与本地 workerd(Miniflare)各跑同一组 12 个场景,另有 2 项 Node 进程内专属测试(未授权绑定、无残留运行)与 6 项只能在 Node 内做的运行器级测试,共 32 项通过。另在云仓 应用/云端编译服务/ 的黑盒测试 测试/编译.test.mjs(7 项)对整个服务全部通过,并与旧 JS 实现做过逐请求差分。
与旧行为的差异
- 请求体不是有效 JSON、为空或超过 2 MiB 时,400 的
error文字是宿主适配的中文消息(“入站 JSON 正文无效:…”“入站 JSON 正文超过 2 MiB”),不再是 JS 引擎的原消息;超过 2 MiB 的请求旧实现会掐断连接,现在返回干净的 400。 - 单个 Worker 进程里同时排队或编译的运行超过 8 个时,第 9 个请求得到 503(消息“编译运行过多”);旧实现无此限额,全部排队。
- 诊断事件里的错误栈含宿主模块路径,两版不同。
- 输入宽容度(应用层,由豫言 JSON 解析与宿主结果桥的限制而来):请求体的多余字段含指数记法的数字(如
1e29)或嵌套超过 32 层时返回 400(旧实现接受);以 UTF-8 BOM 开头的请求体改为接受(旧实现 400)。真实调用方(云端项目 DO)只发files、entry、includeArtifact,不受影响。
未验证与待办
- Cloudflare 边缘的真实内存(128 MiB 限额)与 CPU 表现:本地 workerd 不强制;本机 RSS 对比未见泄漏,峰值与旧实现同量级。
- 真流式:编译器 Wasm 同步运行,会独占 isolate,事件其实要等编译结束才能送出(旧实现同样:响应头与所有行同时到达);接口已是拉取式,宿主日后让出事件循环即可实时;届时应用改用可选内容类型的流式回应(
网页事件流目前固定text/event-stream,可增 0.2.0 函数开始入站文字流回应)。 - 本地
wrangler dev在 8 路并发压测重复数次后会崩溃退出(旧实现同样),不影响单请求正确性;生产未见此类。