事件序号(seq) 协议
别名:
序号 · sequence
事件流里为每条事件分配的**会话内单调递增**的序号,是事件的「可寻址位置」,类似书页码、日志行号或数据库自增主键。客户端凭它精确表达「我从第 N 条之后接着读」,服务端按 `seq > since` 补发增量。seq 是会话内概念:跨会话独立、跨多轮运行连续,但只保证单调、不保证连续(取消/失败会留下「洞」)。见[第 16 章](/chapters/16-resume-v03/)。
它是什么
seq(sequence,事件序号)是事件流里为每条事件分配的会话内单调递增的序号,是事件的「可寻址位置」——类似书的页码、日志行号、数据库自增主键或消息队列的 offset。有了它,客户端才能精确表达「我从第 N 条之后接着读」,服务端才能回答「这个会话现在到几号了」。在第 16 章里,它是断线恢复与后台运行的地基。
三条规则
seq 的语义由三条规则定死:会话内单调递增(第 N+1 条严格大于第 N 条,客户端可以放心地把「最后看到的 seq」当作进度指针);跨会话独立(每个会话各自从 1 开始,s1 的 seq 1 与 s2 的 seq 1 互不相干);跨多轮运行连续(同一会话第 2 轮对话不重新从 1 数起,因为序号计数器活在 loop 之外、按会话持有)。实现上就是 createSeqAllocator 的三行代码:?? 0 让新会话从 1 开始、+ 1 保证递增、分配器注入 loop 外部保证跨运行连续。
常见坑
最容易踩的两个坑:一是把「单调」当「连续」——一轮运行被取消、客户端被 409 拒掉都会留下「洞」(seq 1、2、3、7、8……),客户端只能依赖「seq > since 就补发、seq 大的更新」,绝不能断言「上一跳是 4,这次必然是 5」;二是把 seq 当全局唯一——识别一条事件必须用 (sessionId, seq) 二元组,跨会话比较两个 seq 没有意义。ch10 的 eventId 是同一思想在持久层的化身,换存储时「序号分配在 loop 注入还是存储层」是第一个要重新审视的决策。
小结
一句话:seq 把「有序事件日志」变成可精确寻址、可断点续读的流。它只保证「单调、有序、可寻址」,不保证「恰好一次」——重复投递是协议特性,幂等消费是客户端责任(见第 16 章「常见坑」坑二)。