持久频道接口规范 0.1.0
本包提供同一个持久对象实例内、跨事件的具名文字频道:一个事件订阅并阻塞等待,另一个事件发布,订阅事件被唤醒后转发。云工里每个 fetch、alarm 事件都新建一个豫言 Wasm 实例,事件之间没有内存共享,所以“事件 A 等、事件 B 叫醒 A”这类需求(事件流的实时推送、向长连接下发任务、把回传的分片转给正在读取的调用方)只能借宿主实例内的共享状态完成。本包在这份共享状态上给出保序、可限时等待、可关闭、订阅数可查的最小接口。接口包只依赖标准库。
频道状态属于宿主实例,随持久对象被驱逐而丢失:它不是持久存储,不跨实例、不跨重启。需要持久的事件序列(用于回放)用持久事务或关系数据库保存,频道只负责“已发生事件的即时通知”。本包只能在持久对象事件里使用,其他事件(普通 fetch 等)调用抛豫言异常。
频道与订阅
频道由名字标识:1 至 64 字节,字符限 A-Z a-z 0-9 . _ : -,区分大小写,不合规抛豫言异常。频道在第一个订阅出现时创建,最后一个订阅消失(取消、关闭、被断开)时消失。同一个实例内至多 64 个频道,每个频道至多 256 个订阅:订阅数超限时 订阅持久频道 抛可捕获的豫言异常(多个事件恰好同时越界时,后到者可能改由宿主拒绝);频道数超限由宿主拒绝,云工当前使宿主报错并中止本次运行,不一定能被「尝试运行」捕获。订阅是创建它的事件所拥有的资源,订阅柄是不透明字符串,只在创建它的事件内有效:另一个事件必须自己订阅,拿别处的柄调用抛豫言异常。订阅之后发布的消息一定送达该订阅,订阅之前已发布的消息不补发。每个订阅内消息保序:同一个发布者先发的先到,多个发布者交错时取决于各自发布的先后。
函数
订阅持久频道 订阅一个频道,返回订阅柄。订阅一经返回,之后到来的消息都会为它排队。
发布持久频道文字 把文字发布给频道当前的全部订阅,按订阅先后依次送达,返回送达的订阅数;频道不存在时返回 0。文字 0 至 65536 字节,空文字合法;文字不得以字节 0xFF 起首(合法 UTF-8 从不以它起首),违者抛异常;超过 65536 字节抛异常。已因积压被断开的订阅不计入送达数。
等待持久频道消息 接受订阅柄和超时毫秒(0 至 3600000,越界抛异常),阻塞豫言直到有消息、超时或订阅终结,返回(状态,文字):0 收到消息,文字是消息;1 超时,文字为空;2 频道已被关闭、订阅因积压超过 1 MiB 被断开或订阅已被取消,文字是原因。超时 0 表示立即轮询。状态 2 之后再等仍返回状态 2 与同一原因。频道关闭时,关闭前已排队的消息先交付,之后才得状态 2;订阅因积压被断开时,已排队而未读取的消息全部丢弃。阻塞期间不阻塞其他事件。
取消订阅持久频道 取消订阅并释放资源,幂等;取消之后再等待返回(2,订阅已取消)。应用必须为每个订阅配一次取消,包括异常路径。
关闭持久频道 关闭一个频道:向全部订阅先交付排队的消息,随后各订阅得状态 2 与原因(至多 1024 字节,超限抛异常),返回被关闭的订阅数,频道不存在时为 0。关闭之后同名频道可重新订阅(新一代,与旧订阅无关)。
读取持久频道订阅数 返回频道当前的订阅数,不存在时为 0;它不影响订阅端的等待时长,也不产生订阅端可见的消息。
失败分类:频道名、大小、超时范围、订阅柄、事件种类、订阅数上限等违规抛豫言异常;超时、关闭、被断开、已取消属可恢复的业务情形,用等待的返回状态表达。
先订阅、再回放、再合并实时(正确用法)
事件流服务要“连上后先补历史、再接实时”,且不能丢也不能重。正确的步骤是:
- 发布者约定先把事件持久化(得到单调递增的序号),再发布,消息里带序号;
- 订阅者先订阅频道,得到订阅柄——此后发布的消息一定不会丢;
- 再读持久存储:最大序号(称“头”)与
after之后至头的历史,回放给客户端; - 之后循环等待订阅:消息序号 ≤ 头者已经在历史里回放过,跳过;序号 > 头者转发;
- 结束前取消订阅。
订阅之前提交并发布的事件在历史里;订阅之后提交的事件从订阅收到;提交在读头之前、发布在订阅之后的事件两边都有,以序号去重。多个发布者并发时,实时消息的到达次序可能与序号略有出入,客户端应以序号为准。订阅积压超过 1 MiB 会被断开:回放很慢或事件很密时,应准备好在得到状态 2 后重新订阅并从最后转发的序号继续。
限制与生命周期
订阅不随事件结束自动回收(当前云工宿主):未取消的订阅继续占用名额并接收消息,直到积压被断开或实例被驱逐;事件因宿主时限或平台取消而提前终止时应用也来不及取消。这是宿主待改进项(建议宿主在事件结束时取消该事件遗留的订阅)。因此频道名应取有限个固定名字,不要把请求号、用户号等无界量编进频道名(消息里带号,订阅端自行过滤)。云工每个事件的豫言实例有墙钟时限(默认 30 秒,应用目录的 执行配置.json 可改到至多 15 分钟);阻塞等待可以超过时限,但醒来后下一次调用宿主即失败,所以等待与转发的循环应在时限之内结束。以上云端行为除注明本地 workerd 已验者,均“待云端验证”。
跨宿主一致性要求
各宿主须给出相同的返回值语义与异常类别:订阅后到来的消息必达、订阅内保序、送达数的含义、状态 0/1/2 的含义与优先次序、关闭时先交付后终止、积压断开丢弃排队消息、幂等的取消。宿主可以不同的只有上限数值的具体实现方式(须不低于上述数值或明确报告)、内部缓冲和调度。
一致性样例
- 事件甲订阅
c;事件乙先后发布a、b,各返回 1;甲两次等待依次得(0,a)、(0,b)。 - 无订阅时发布返回 0;订阅之前发布的消息不补发。
- 甲等待 200 毫秒而无消息,得(1,空),用时约 200 毫秒;期间读取订阅数不改变这一长短。
- 乙以原因
收摊关闭c:甲先收到已排队的消息,再得(2,收摊);再等仍是(2,收摊);同名可重新订阅。 - 甲订阅后不读取,乙连续发布超过 1 MiB:之后甲的等待得(2,含“积压”的原因),乙的发布不再送达甲。
- 同一频道第 257 个订阅抛可捕获的豫言异常;第 65 个频道由宿主拒绝。
- 事件乙用事件甲的订阅柄调用等待或取消,抛豫言异常。
- 发布 0 与 65536 字节成功;65537 字节、字节 0xFF 起首的文字失败;频道名不合规失败。
- 回放范例:事件丙先订阅,再读头与历史;事件丁在此窗口内追加并发布若干事件,丙的输出里每个序号恰好出现一次。
边界
不做持久存储、跨实例广播、消息确认或重传、序号分配、过期、订阅过滤与通配、二进制内容、限速;不解析消息;不保证事件结束时自动取消订阅(当前);不授予任何网络或存储能力。