# 自动 Worker 接入 更新:2026-09-17。自动 Worker 已实现,并通过真实 MySQL、实际服务命令、WSS 断线/ACK 丢失/持续断网及 [进程强杀恢复回归](../reports/phase-2.md)。[尝试与证据查询](../api/execution-history.md) 可核对结果,协议形状见 [Schema](../api/worker-schema.md)。 源码:[运行器](../../sdk/go/worker_runtime.go)、[配置与中文注释](../../sdk/go/worker_runtime_types.go)、[完整示例](../../sdk/go/examples/worker/main.go)。特殊传输集成可以使用 [底层协议 SDK](worker.md),普通消费直接使用本页的 Worker。 ## 1. 运行完整示例 按 [底层 SDK 的初始化步骤](worker.md#1-可运行的文本规范化示例) 创建具有 Worker 权限的专用凭据,在仓库根目录执行: ```sh cd sdk/go ORBIT_URL=http://127.0.0.1:8080 \ ORBIT_CREDENTIALS_FILE=../../.local/worker.credentials.json \ go run ./examples/worker ``` 示例提交三条文本任务,以容量 2 启动自动 Worker,真实执行文本规范化,查询结果后取消 Run context 并等待排空。预期输出: ```text task=<任务ID> state=SUCCEEDED result={"text":"HELLO ORBIT"} task=<任务ID> state=SUCCEEDED result={"text":"REUSABLE WORKER"} task=<任务ID> state=SUCCEEDED result={"text":"DURABLE RESULT"} worker_drained=true started=3 confirmed=3 running=0 ``` 每次示例使用独立操作和能力名称,避免接走以前运行留下的任务;任务与审计历史保留。生产通常使用固定处理器版本,由独立长驻 Worker 消费;本示例把生产者和消费者放在一个命令里方便复现。 ## 2. 注册处理器与启动 以下为接入函数片段;函数收到的 ctx 应来自进程信号或应用生命周期,并等待 Run 返回后再退出进程。 ```go worker, err := orbit.NewWorker(client, orbit.WorkerOptions{ QueueID: queueID, Capacity: 4, ShutdownTimeout: 30 * time.Second, Handlers: []orbit.WorkerHandler{{ Capability: orbit.WorkerCapability{ Handler: "text.normalize", HandlerVersion: "v1", PayloadVersion: "v1", }, Handle: func(ctx context.Context, execution orbit.WorkerExecution) (json.RawMessage, error) { // 实际 HTTP/数据库/文件操作应使用这个 ctx,并实现业务效果幂等。 if err := ctx.Err(); err != nil { return nil, err } return json.RawMessage(`{"processed":true}`), nil }, }}, }) if err != nil { return err } return worker.Run(ctx) ``` 任务的 handler、handler_version、payload_version 必须和注册能力精确匹配。处理器收到 Task、AttemptID 与执行 Epoch,不收到实例/尝试秘密。Task.ID 或接入方保存的业务操作键用于效果去重;AttemptID 标识一次尝试,不能作为跨尝试的业务幂等键。资源侧支持执行代次校验时,可拒绝旧 Epoch 的写入。 SDK 的 Run 每个实例只能调用一次,包括已经正常退出的实例。重连由内部保留原身份处理;不要通过反复 NewWorker 来代替断线恢复。进程重启会生成新身份,不能继续使用旧进程的内存状态来声称已经完成。 ## 3. 配置和接口 | 配置 | 默认 / 范围 | 含义 | | --- | --- | --- | | QueueID | 必填,32 位 ID | 当前凭据有消费权限的队列 | | Capacity | 4;1–256 | 同时尚未返回的处理器上限;不响应取消的函数仍占物理槽 | | Handlers | 1–32 项 | 精确能力,重复能力和空函数在构造时拒绝 | | ShutdownTimeout | 30 秒;1 毫秒–5 分钟 | 从 Run context 取消起计算的整体收尾期限 | | ReconnectMinDelay | 200 毫秒;至少 10 毫秒 | 连接失败退避起点 | | ReconnectMaxDelay | 5 秒;不小于起点,最多 30 秒 | 指数退避上限,区间抖动;不增加任务尝试次数 | `Run(ctx)` 正常排空返回 nil;同实例再次运行返回 ErrWorkerAlreadyRun。`Stats()` 返回进程内 Running、Started、RenewalsConfirmed、ReportsConfirmed、EvidenceConfirmed、Reconnects、DroppedEvents 快照,不是持久化业务成功率。ReportsConfirmed 包含已确认的失败/未知尝试报告,一条重试任务可能计多次。 `Events()` 是 128 条缓冲的只读本地通知流,不读取也不会阻塞运行;缓冲满时丢弃并计数,Run 返回时关闭。通知只含类型、任务/尝试 ID 与稳定分类,没有业务结果或秘密。它不能代替审计、可靠结果订阅或任务查询。 ## 4. 执行、续租与恢复 - 只有 Start 当前授权有效才调用处理器;同进程中的已知尝试最多调用一次。重复确认与重新连接只核对控制操作。 - 处理器在独立 goroutine 执行,网络控制由事件循环管理;每连接当前只保留一个执行器控制请求在途。不同处理器可以并行,控制请求顺序与业务并发上限分开。 - 会话心跳按服务端 TTL 的三分之一计算,上限 5 秒。任务续租按当前保守租约剩余时间的三分之一安排,下限 20 毫秒;服务器仍限制总截止与单次尝试时限。 - 每个处理器的租约监视器独立取消 context,断网或控制请求阻塞也不会延长本地执行权限。最新会话若缩短租约,监视器会采用较短期限。已到期的处理器不会被后续确认复活。 - 重连复用实例和尝试身份,在开启容量之前 Resume 所有已知尝试;待确认 start/renew/finish 使用原消息 ID。认证、权限及协议错误终止 Run;临时服务/连接错误按退避核对。 - 结果未确认或处理器失去租约时,运行器降低对外声明的容量。仍未返回的函数占用真实并发槽;已在传输中的预占会有界接收,已知尝试最多为配置容量的两倍。 Run 的重连恢复同一次控制操作。平台的 [安全重试](../api/retries.md) 使用同一 Task.ID 派发新的 AttemptID/Epoch,运行器按新的有效授权调用处理器,预算与退避由服务端持久化管理。 ## 5. 结果与错误分类 | 处理器结果 | SDK 报告 / 服务端行为 | | --- | --- | | 合法非 null JSON + nil error | SUCCEEDED | | `&orbit.WorkerFailure{Code:"OBJECT_REJECTED", Details:...}` | FAILED;默认 Retryable=false,Details 为空时为 `{}` | | `&orbit.WorkerFailure{Code:"TEMPORARY_REJECTION", Retryable:true}` | 明确失败可安全重试,平台仍核验次数、总时限和历史不确定效果 | | 普通 error | UNKNOWN / HANDLER_ERROR;默认转核查,仅 IDEMPOTENT 且预算允许时可自动重试 | | panic / runtime.Goexit | UNKNOWN / HANDLER_PANIC 或 HANDLER_ABORTED | | 非法 JSON、超限结果、不支持数值或嵌套 | UNKNOWN / INVALID_HANDLER_RESULT | | 处理器因租约取消返回错误 | UNKNOWN / LEASE_LOST | | 报告到达时租约已失效、或结果与权威状态冲突 | 服务端可只保存 EVIDENCE_ONLY;不把证据计为任务成功 | 原始异常和 panic 内容不写入结果或通知。Result/Details 最多 256 KiB,应优先使用对象引用。SDK 保留精确数字文本,并按服务端数字范围及完整帧的嵌套上限预检,避免产生永远无法接受的报告。 普通网络错误不能证明副作用未发生,因此不会自动解释为明确失败。UNKNOWN 由服务端结合真实幂等声明决定是否重试;迟到证据不能清除历史不确定效果。完整规则及可运行示例见 [安全重试](../api/retries.md)。 ## 6. 退出与故障边界 取消 Run context 后,运行器请求持久化排空,收集排空 ACK 之前已派发的工作,继续为在途任务续租并确认结果。进程应等待 Run 返回,而不是取消 context 后立即退出。 ShutdownTimeout 从取消信号发生时开始计时,包含网络等待和处理器收尾。到期后取消控制请求及处理器 context,返回 `*WorkerRunError`;可用 `errors.Is(err, orbit.ErrWorkerShutdownTimeout)` 判断,Unconfirmed 和 Running 给出终止收尾时的快照。其他致命错误也带这些计数,便于识别未核对的执行。 Go 无法杀死忽略 context 的 goroutine。Run 可以有界返回,但这类函数可能仍在进程中执行;不得把返回错误解释为副作用已停止,也不要在同一进程盲目重建 Worker 叠加执行。服务端对失联执行按租约回收,依冻结安全策略决定重试或进入核查。 身份、尚未确认的结果和消息 ID 当前保存在内存,支持同进程重连;进程被强杀会失去这些内容。持久化租约、操作回执、最终结果和审计在 MySQL 中,业务外部效果仍需要业务幂等或查询核对。这一边界不等于端到端 exactly-once。 运行器细节见 [自动运行器回归报告](../reports/phase-2-worker-runtime.md),后续真实进程故障证据见 [P2 阶段验收](../reports/phase-2.md)。实际部署中的多主机故障、数据库复制确认与容量承诺仍需后续生产验收。