Files
orbit/docs/sdk/event-consumer.md

8.0 KiB
Raw Blame History

Go 结果消费 SDK

对应 HTTP/WSS 契约。SDK 是独立模块,只依赖 HTTP/WebSocket,不导入服务端模型、存储或数据库驱动。订阅先用管理 API 创建,并固定在创建凭据与队列范围内。

已提供 EventConnection、EventConsumer、公开类型 与 可运行协议示例。独立 MySQL Inbox 示例 已提供业务事务去重,并验证消费者提交后、ACK前被强杀的恢复;SDK内存状态仍不能代替Inbox。

1. 自动消费

下面是可复用的应用函数。applyOnce 由接收方提供,必须在同一数据库事务内保存事件去重记录和业务变更;事务明确提交或读取到已经提交的同一事件后才返回 nil。它不能仅向内存队列投递后返回。

func consumeResults(
    ctx context.Context,
    client *orbit.Client,
    subscriptionID string,
    applyOnce func(context.Context, orbit.ResultEvent) error,
) error {
    consumer, err := orbit.NewEventConsumer(client, orbit.EventConsumerOptions{
        SubscriptionID: subscriptionID,
        Capacity: 4,
        Handle: applyOnce,
        ShutdownTimeout: 30 * time.Second,
        ReconnectMinDelay: 200 * time.Millisecond,
        ReconnectMaxDelay: 5 * time.Second,
    })
    if err != nil { return err }
    return consumer.Run(ctx)
}

所在文件需导入 context、time 和 orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"。同一 Consumer 只能 Run 一次;多实例可共同绑定同一个订阅,按服务端交付租约竞争,不保证事件处理/完成顺序。

配置 默认 / 范围
SubscriptionID 必填;有效的 32 位小写十六进制 ID
Handle 必填;处理冻结的 ResultEvent,不接收交付秘密
Capacity 默认 4;1–32 个本地在途事件及处理器
ShutdownTimeout 默认 30 秒;1 毫秒至 5 分钟
ReconnectMinDelay 默认 200 毫秒;至少 10 毫秒
ReconnectMaxDelay 默认 5 秒;不小于起点且不超过 30 秒

每个连接另有 Capacity 条未读交付缓冲,满后关闭连接并按退避重连。处理器结果缓冲不超过 Capacity,本地观察缓冲 128;不会为积压无限创建 goroutine。自动消费器每条连接至多一个 ACK 在途,心跳独立并发,慢 ACK 不阻塞 WebSocket 读取。

2. 提交、补发和去重

推荐的接收方事务步骤:

  1. 使用固定消费者用途与事件 ID 建立唯一键,例如 (consumer_name, event_id)。不要使用连接 ID、交付代次或消息 ID 作为业务去重键。
  2. 在一个业务事务中插入 Inbox 记录并更新业务数据。唯一键竞争必须等待对方事务提交或回滚;遇到重复键时核对已有记录,而不是忽略所有 SQL 错误。
  3. 业务事务提交成功后返回 nil,SDK 再发送 ACK。提交结果未知时返回 error,保留核查信息,下次投递先按唯一键核对。
  4. ACK 已提交但回应丢失,SDK 保留原证明,在新连接上重报;在该本地事件仍保留期间不会重新调用处理器。
  5. 原证明已被新代次替代时,SDK 记录 proof_lost,等待新投递。新投递会再次进入处理器,必须命中持久化 Inbox 后才能确认新证明。

处理器 error、panic 或 Goexit 都不会 ACK,也不会在 SDK 内存中立即重新执行。平台在原 30 秒租约自然到期后,按退避和累计预算重新投递;耗尽后保留 EXPIRED,占用订阅窗口,需要管理 API 追加预算。仅在启用订阅有领取活动时触发恢复,当前尚无独立后台交付清扫器。

事件交付租约不提供业务独占执行授权,Ping 不延长它。长于租约的处理、网络分区或多个接收进程可能造成同一事件并行进入业务侧;本地 Capacity 和事件合并不能代替跨进程的唯一约束及事务。外部 HTTP 副作用也不能和 Inbox SQL 自动组成原子事务,需要接收方 Outbox、业务幂等及结果核查设计。

ResultEvent.Snapshot 是原事件时刻的冻结快照,不能通过后来查询到的当前任务状态替换。SDK 保留 RawMessage 中大整数及业务 JSON;例如 9007199254740993 不经 float64 重编码。task.needs_review 表示需要核查,不能按业务成功或最终失败处理。

3. 重连与关闭

自动心跳间隔为当前 v1 固定的 15 秒。心跳也重新验证凭据和订阅权限,暂停的订阅仍可心跳及确认已有交付。

网络中断、超时、接收缓冲耗尽、UNAVAILABLE、HTTP 408/429/5xx 使用指数退避及半区间抖动重连;延迟上限为 ReconnectMaxDelay。确认成功后重置退避。认证、权限、资源不存在、历史缺口、协议错误及其他永久错误停止消费,返回支持 errors.Is/As 的 EventConsumerRunError。

取消 Run 的 context 后,SDK 停止启动新处理器并取消已有处理器 context。在 ShutdownTimeout 内收集处理器返回,已提交的结果仍尝试 ACK 或重连核对;正常收尾返回 nil。未成功处理的交付保留给平台按预算恢复。

超时返回 ErrEventShutdownTimeout 包装错误:Unconfirmed 是本进程已知且未确认的事件数,Running 是仍未退出的处理器数。Go 不能强杀忽略 context 的函数,不能在收到超时后声称这些函数的业务效果已经撤销。永久连接错误也会取消并等待在途函数,等待受相同收尾时限约束。

调用方主动停止后,暂停订阅是独立管理操作;关闭一个 Consumer 不会暂停共享订阅。进程被强杀时,SDK 内存中的结果和证明不保证保留,接收方 Inbox 必须独立持久化。

4. 底层连接

方法 语义
Client.DialEvents(ctx, EventRegistration) 使用 Authorization 与 orbit.events.v1 握手;ctx 只控制握手,容量必须显式传 1–32
Welcome() 初始握手快照;不是实时订阅状态
Next(ctx) 等待交付;等待取消不关闭连接
Ack(ctx, messageID, EventProof) 确认原交付,可在同订阅的新连接中重报
Ping(ctx, messageID) 查询 Enabled 并维持连接,不续交付租约
Done() / Err() 等待读取循环退出及查询关闭原因
Close() 关闭传输并等待读取循环退出,不取消服务端交付

EventConnection 不自动心跳、重连或执行处理器。应用必须每 15 秒 Ping,并在退出时 Close。最多 32 个在途控制请求;超出返回 ErrEventBusy。控制请求总时限最多 10 秒,包含写入排队;握手最多 8 秒;每条文本帧最多 512 KiB。控制超时关闭连接,防止无法关联的迟到回应被当作另一次操作成功。

grant.Proof() 包含订阅、事件、交付、代次和秘密;SDK 本地核对订阅与回执事件,线上 ACK 只发送交付 ID、代次和秘密。重连后使用原 Proof,可通过 NewEventMessageID 生成新的关联 ID。ACK 持久化去重不依赖消息 ID。

SDK 严格验证消息方向、会话、回复关联、规范十进制 uint64、秘密编码、必填字段、重复键、字段大小写和交付/事件/快照关联。无效响应返回 ErrEventProtocol 并关闭连接;不会将无效回执当作已确认。

5. 观察与敏感信息

Stats() 返回进程内 Running、Started、HandlerFailures、Acknowledged、Reconnects 和 DroppedEvents;重启清零。Events() 返回有界本地通知,不读取也不会阻塞执行,Run 返回时关闭。它们不能替代数据库状态和持久化审计;Acknowledged 包括对已提交 ACK 的重报确认。

EventGrant、EventProof、EventConnection、EventConsumer 和 EventError 的常见 fmt 输出隐藏秘密、业务 JSON 或远端错误正文。显式 JSON 序列化 Grant/Proof 仍包含租约秘密,不可放到普通日志;若业务确需保存 Proof,应加密并限制访问。调用方记录自己的 handler error 时也应脱敏,SDK 观察事件只给出 HANDLER_FAILED 分类。