豫豫言文档API ATLAS
入口豫言操作系统持久频道适配。包。豫
2 模块43 名称
全部模块

豫言 / 豫言操作系统持久频道适配 @ 0.1.0

以云工宿主的具名广播、可读流读取器与 Promise.race 实现持久频道接口:订阅、保序发布、限时等待、关闭与订阅数。

说明.汉语.md

# 持久频道云工适配

`频道。豫` 用豫言写在云工宿主库的具名广播、可读流读取器与 Promise 之上,**不增加任何宿主 JavaScript**。它实现[持久频道接口规范](../../../豫言操作系统接口/持久频道/规范.汉语.md) 0.1.0:订阅、保序发布、限时等待、取消、关闭与订阅数。

## 实现

- **宿主频道**:每个逻辑频道对应宿主广播 `持久频道:<名>`(前缀使它与应用直接使用的 `云工广播新流` 频道互不相通)。宿主广播属于宿主对象,即同一个 Durable Object 实例:注册表随对象存续,被驱逐即丢失;每实例至多 64 个频道,每频道至多 256 个读者,单块 64 KiB,每读者积压 1 MiB。
- **帧**:消息文字的 UTF-8 字节**原样**作为一块发布,不加头,所以文字额度就是宿主的 64 KiB;控制帧以字节 `0xFF`(合法 UTF-8 绝不以它起首)起首:`FF 02` 是探测帧,`FF 01` 加原因文字是关闭帧。发布时拒绝以 `0xFF` 起首的文字。
- **订阅数与订阅上限**:宿主没有“数订阅”的原语,本适配向频道发一个探测帧,用宿主返回的送达数作订阅数(订阅端读到探测帧直接跳过,剩余超时按已用时间重算,所以探测不会拉长或缩短等待)。`订阅持久频道` 先探测,达 256 时抛可捕获的豫言异常;频道数超 64 无法预先得知,由宿主拒绝(宿主报错,不可捕获)。
- **等待与超时**:对订阅读取器发 `read()`,用 `Promise.allSettled` 包装(不产生未处理拒绝),与 `scheduler.wait` 计时(自己的 AbortController,结束即撤销)做 `Promise.race`;超时**不丢弃**尚未完成的读取承诺,下一次等待继续使用它,因此不会丢消息。读到的字节经 `云工句柄取字节` 取出后立即释放句柄,每次等待都释放临时句柄(宿主每事件句柄上限 4096,单订阅收发数千条已验)。
- **关闭**:先向全部订阅发关闭帧(带原因),再关闭宿主频道,订阅端读完排队消息后得状态 2。积压被宿主断开时,订阅端读取以错误告终,适配映射为“订阅积压超过 1 MiB,已被断开”。
- **登记簿与订阅柄**:每个 Wasm 实例内一张订阅登记簿(柄、读取器号、待读承诺号、终态、终因)。订阅柄是“实例标记:读取器句柄号”,实例标记取 `crypto.randomUUID()` 的前 13 个字符,另一个事件拿到它得到豫言异常。取消用 `云工调用方法安全` 调 `cancel`(即使流已因积压出错也不会让宿主异常穿出),并释放读取器句柄;从不调用 `releaseLock`。
- 只能在持久对象事件里使用:事件种类须以 `durable-` 起首,否则抛可捕获的豫言异常(宿主原语本身在非持久对象事件里会报不可捕获的错误)。
- 所有内部辅助名都以 `持久频道内` 起首。

## 部署要求

1. 全局 `scheduler.wait`:Workers 自带;Node 宿主用 `node:timers/promises` 的 `scheduler` 垫片经 `创建云工宿主({全局})` 传入,见 `一致性验证/工具.mjs` 的 `造全局`。
2. 事件时限:阻塞等待可以超过每事件墙钟时限(默认 30 秒),但醒来后下一次调用宿主即失败;需要更久的等待与转发循环,在应用目录放 `执行配置.json`(至多 15 分钟)。

## 本地 workerd 实测(Wrangler 4.129.0,兼容日期 2026-09-10,同一个持久对象实例上的多个并发 `durable-fetch`,真实 HTTP 与真实 D1)

- 跨请求订阅与发布、三订阅者按序各得 100 条、超时精度(300 毫秒得 290 至 700 毫秒内,探测不改变长短)、关闭先交后终、64 KiB 消息与**零长消息**(空文字)均正确;订阅上限 256 与积压断开的原因文字正确;一个订阅内收发 3000 条不耗尽句柄;先订阅再回放的窗口去重在真实 D1 上恰好各一次。
- **客户端断开之后持久对象里的事件继续运行**:订阅事件在客户端走后仍被频道消息唤醒,写入前两次仍返回 0,第三次返回 1,随后应用取消订阅(订阅数归 0)。等待期间(无写入)断开不会唤醒任何东西,只有消息或超时到来才会再次运行——所以长连接转发要靠心跳与周期超时。
- **订阅不随事件结束回收**:`订阅不取消` 试验路由订阅后直接返回,订阅数仍为 1;一个专属实例里 64 个这样的频道之后,再订阅新频道得到 HTTP 500(宿主拒绝:`云工广播频道达到上限`)。这能把实例的 64 个频道名额永久耗尽(直到对象被驱逐),事件被 30 秒时限或平台取消提前终止时同样发生。
- 云端(Cloudflare 边缘)行为待验证。

## 宿主改动请求(写给宿主壳维护者;本适配不改宿主 JS)

1. **事件结束时收尾流写器与读取器**:详见[网页事件流适配说明](../网页事件流/说明.汉语.md)“宿主改动请求”的 `收尾流` 补丁;对读取器一律 `cancel()`,订阅即被回收,本适配的订阅登记(读取器句柄)正好落在补丁扫描范围内。补丁宿主上持久频道的一致性验证全部通过,“事件结束时应自动回收订阅”那条测试由待办转为通过。
2. **(可选,更稳)新增两个原语**,让上限成为可捕获的异常并免去探测帧:`豫言_云工_广播新流安全(名) → [爻, 流号或错误文]`(把订阅数与频道数超限转成返回值)与 `豫言_云工_广播订阅数(名) → 整数`(读取 `广播频道` 里该频道现有读者数,不产生任何帧)。适配届时可改用它们,接口不变。

## 一致性验证

`一致性验证/` 含试验应用 `持久频道一致性应用`(真实 Wasm)、Node 测试 `持久频道一致性.test.mjs`(同一宿主对象上并发多个 `durable-fetch` 模拟同一个持久对象的多个事件:跨事件订阅与发布、多订阅者、多发布者保序、超时与探测、零超时、关闭、取消、无效与他事订阅柄、256 与 64 上限、积压断开、上万条消息、文字规则、频道名规则、非持久对象事件、实例隔离、先订阅后回放去重范例)、组合范例 `服务商连接缩影应用`(用两个包复现 AI 服务商连接持久对象:`/connect` 事件流带心跳、`/dispatch` 与 `/chunk` 是另外的事件、新连接替换旧连接)及其测试,以及真实 workerd 复验驱动。复跑方法见该目录说明。

说明.文言.md

# 持久频道云工适配

`频道。豫` 以豫言书于云工宿主库之具名广播、可读流读取器与 Promise 之上,**不增宿主 JavaScript 一字**。其行[持久频道公约](../../../豫言操作系统接口/持久频道/规范.文言.md) 0.1.0:订、保序而发、限时而候、撤、闭与订数。

## 所以行之

- **宿主之频道**:每逻辑频道对宿主广播 `持久频道:<名>`(其前缀使之与应用直用 `云工广播新流` 之频道不相通)。宿主广播属宿主对象,即同一 Durable Object 实例:注册表随对象而存,被逐则失;每实例至多六十四频道,每频道至多二百五十六读者,单块六十四 KiB,每读者积压一 MiB。
- **帧**:讯文之 UTF-8 字节**原样**为一块而发,不加头,故文之额即宿主之六十四 KiB;控制帧以字节 `0xFF`(合法 UTF-8 绝不以之起首)起首:`FF 02` 为探帧,`FF 01` 加因文为闭帧。发时拒以 `0xFF` 起首之文。
- **订数与订限**:宿主无“数订”之原语,本适配向频道发一探帧,以宿主所返之所达数为订数(订者读得探帧即略之,余时按已用者重算,故探不使候久或暂)。`订阅持久频道` 先探,至二百五十六则抛可捕之豫言异;频道数逾六十四不可先知,由宿主拒之(宿主报误,不可捕)。
- **候与逾时**:向订之读取器发 `read()`,以 `Promise.allSettled` 裹之(不生未处理之拒),与 `scheduler.wait` 计时(自有之 AbortController,毕即撤)作 `Promise.race`;逾时**不弃**尚未成之读承诺,下次候续用之,故不失讯。所读之字节经 `云工句柄取字节` 取出即释其柄,每候皆释临时之柄(宿主每事句柄之限四千零九十六,一订之内收发数千条已验)。
- **闭**:先向诸订发闭帧(带因),后闭宿主频道,订者读尽所排之讯而得状态二。积压为宿主所断者,订之读以误告终,适配映之为“订阅积压超过 1 MiB,已被断开”。
- **登记簿与订阅柄**:每 Wasm 实例内一订阅登记簿(柄、读取器号、待读承诺号、终态、终因)。订阅柄为“实例标记:读取器句柄号”,实例标记取 `crypto.randomUUID()` 之首十三字,他事持之则得豫言之异。撤用 `云工调用方法安全` 调 `cancel`(纵流已因积压而误,亦不使宿主之异穿出),并释读取器之柄;终不调 `releaseLock`。
- 惟可用于持久对象之事:事类须以 `durable-` 起首,否则抛可捕之豫言异(宿主原语于非持久对象之事自报不可捕之误)。
- 内部诸辅助之名皆以 `持久频道内` 起首。

## 部署之要

一、全局 `scheduler.wait`:Workers 自具;Node 宿主以 `node:timers/promises` 之 `scheduler` 垫片经 `创建云工宿主({全局})` 授之,见 `一致性验证/工具.mjs` 之 `造全局`。
二、事之限:阻候可逾每事墙钟之限(默认三十秒),然醒后下一调宿主即败;需更久之候与转之循环者,于应用目录置 `执行配置.json`(至多十五分钟)。

## 本地 workerd 之实测(Wrangler 4.129.0,兼容日 2026-09-10,同一持久对象实例上诸并发 `durable-fetch`,真实 HTTP 与真实 D1)

- 跨请求订与发、三订者按序各得百条、逾时之精(三百毫秒而得于二百九十至七百毫秒间,探不改其久暂)、闭而先交后终、六十四 KiB 之讯与**零长之讯**(空文),皆正;订限二百五十六与积压断之因文皆正;一订之内收发三千条不尽宿主句柄;先订后放之窗去重,于真实 D1 恰各一次。
- **客断之后持久对象内之事续行**:订者之事于客去后犹为频道之讯所唤,其书先二次仍返零,第三次返一,而后应用撤订(订数归零)。阻候之时(无书)断不唤何物,惟讯至或逾时至方复行——故长联之转,赖心跳与周期之逾时。
- **订不随事毕而收**:`订阅不取消` 之试验路由订后径返,订数仍一;一专属实例中有六十四此类频道之后,再订新频道得 HTTP 500(宿主拒:`云工广播频道达到上限`)。此可使实例六十四频道之额永尽(直至对象被逐),事因三十秒之限或平台取消而早终者亦然。
- 云端(Cloudflare 边缘)之行待验。

## 宿主改动之请(致宿主壳之守者;此适配不改宿主 JS)

一、**事毕,收尾流之写器与读取器**:详见[网页事件流适配说明](../网页事件流/说明.文言.md)“宿主改动之请”之 `收尾流` 补丁;读取器一律 `cancel()`,订即被收,本适配之订登记(读取器句柄)正在补丁所扫之内。补丁宿主上持久频道之一致性验证尽通过,“事毕订当自收”之测由待办转通过。
二、**(可选,更稳)增二原语**,令限成可捕之异而免探帧:`豫言_云工_广播新流安全(名) → [爻, 流号或误文]`(订数与频道数逾限转为返值)与 `豫言_云工_广播订阅数(名) → 整数`(读 `广播频道` 中该频道今有之读者数,不生一帧)。适配届时可改用之,接口不变。

## 一致性验证

`一致性验证/` 含试验应用 `持久频道一致性应用`(实 Wasm)、Node 测试 `持久频道一致性.test.mjs`(同一宿主对象上并发诸 `durable-fetch` 以拟同一持久对象之诸事:跨事订与发、多订者、多发者保序、逾时与探、零超时、闭、撤、无效与他事之订阅柄、二百五十六与六十四之限、积压断、万条之讯、文字之则、频道名之则、非持久对象之事、实例相隔、先订后放去重之范)、组合范例 `服务商连接缩影应用`(以二包复 AI 服务商连接持久对象:`/connect` 事件流带心跳、`/dispatch` 与 `/chunk` 乃他事、新联代旧联)及其测试,并有真实 workerd 复验之驱动。复行之法见该目录说明。