跳转至

Gateway 锁

为什么

  • 只有一个 Gateway 进程应拥有一个状态目录;运行额外的 Gateway 时,请使用隔离的配置档、状态目录、配置和端口。
  • 在崩溃或 SIGKILL 后,当记录的进程被确认已死亡时,恢复所有权。
  • 当另一个 Gateway 已经拥有该端口时,快速失败并给出明确错误。

三层

启动会在发布兼容性元数据并绑定其监听器之前建立状态所有权:

每次服务器启动都使用相同的准入机制,包括由容器引导启动的临时 Gateway。直接服务器在关闭完成之前保留所有权。托管服务器使用运行循环中已有的所有者,该所有者仍负责重启交接和释放;关闭一个服务器代际不会释放该所有者。

  1. 进程所有者独占地创建一个以规范共享状态数据库路径为键的 sidecar。Gateway 启动、嵌入式代理和离线维护竞争同一个所有者。OPENCLAW_ALLOW_MULTI_GATEWAY=1 不允许共享可变状态。
  2. 兼容性投影在历史状态本地锁中发布所有者的 PID、进程启动标识、角色和运行时端口。受支持的旧版 Gateway 使用它来检测当前进程。它是进程所有者下的元数据,而不是独立的生命周期所有者。
  3. 套接字绑定将 HTTP/WebSocket 监听器(默认 ws://127.0.0.1:18789)绑定为独占的 TCP 监听器。

在启动或重启期间,Gateway 最多等待五分钟,等待另一个 OpenClaw 进程释放状态所有权。它会记录等待开始的时间,以及所有权获取或等待超时的时间。

状态和配置锁

  • 在 Linux 和 macOS 上,进程所有者位于 $OPENCLAW_STATE_DIR/tmp/openclaw-<uid>/state.<hash>.lock。它使用所选的状态存储,无需对其父目录具有写访问权限,也不依赖系统临时目录。在 Windows 上,它位于用户的 AppData/Local/OpenClaw/locks/openclaw-state-owners 目录下。哈希标识规范共享状态数据库路径。TMPDIR 不会更改此命名空间。
  • 破坏性清理在移除状态内容的同时保留进程所有者和兼容性投影,因此新的启动会保持阻塞,直到原生数据库资源和破坏性操作稳定为止。正常释放会移除 sidecar;没有 SQLite 协调数据库伴随它们。
  • 清理在删除状态之前拒绝重定向的数据库或运行时锁路径:删除内部符号链接可能会选择一个新所有者,而原始文件仍被锁定。重试前请选择实际的状态根目录和真实的内部目录。仍然支持整个状态根的别名。
  • 所有权使用独占文件创建以及 fs-safe 现有的受检释放和过期恢复协议。已死亡进程或经验证已更改的进程启动标识允许恢复。仅凭年龄永远不会撤销活动所有者,且不可读的所有权仍被视为拒绝。
  • 专用的共享状态读取传输可以复用成功的进程所有者和兼容性投影验证,持续时间不到一秒,同时使用相同最大年龄的规范所有者路径解析。到期时,读取准入会同步解析路径,并在分发工作线程 I/O 之前验证 sidecar。此界限不依赖定时器或事件循环调度。释放、清理和模式维护转换会立即使缓存路径失效。没有正在服务的本地进程所有者的读取保留新鲜验证。瞬时验证错误会拒绝受影响的读取;下一次读取会重试,而不是永久禁用所有者。
  • 写入、模式和运维转换、租约授予以及每个通用 SQLite 工作线程任务都保留其立即的新鲜所有权检查。缓存的读取验证不能替代物理数据库身份、调用方授权、原生资源保管或事务/提交准入。
  • 模式/引导工作从活动的本地 Gateway 或维护所有者借用保留的权限。如果没有,它会短暂获取相同的进程门和历史投影,因此新的 CLI 无法在受支持的旧版 Gateway 之下迁移状态。已接受的工作在稳定之前保留两者,即使其根停止出借权限。
  • 兼容性投影是 $OPENCLAW_STATE_DIR/tmp/openclaw-<uid>/gateway.state.lock(在没有用户 ID 的平台上为 openclaw)。新运行时不会创建历史按配置锁或任何 .sqlite 锁伴随文件。发现机制仍会为受支持的旧运行时读取历史配置和状态锁。
  • 活动所有者会在任一进程绑定其端口之前阻止另一个启动。如果等待超时,启动会报告:
GatewayLockError("failed to acquire gateway state ownership; waited <ms>ms for Gateway state ownership")

套接字绑定

  • 在 EADDRINUSE 时,启动会以 500ms 间隔重试绑定,最多 20 次(总计约 10 秒),以熬过最近退出进程后的 TIME_WAIT 窗口。
  • 如果重试后端口仍在使用:
GatewayLockError("another gateway instance is already listening on ws://127.0.0.1:<port>")
  • 其他绑定失败:
GatewayLockError("failed to bind gateway socket on ws://127.0.0.1:<port>: <cause>")

在关闭时,Gateway 会先关闭其服务器并完成其拥有的工作,然后释放其进程所有者和兼容性投影。离线维护会关闭准入,并在状态、关联配置/凭据和别名移除过程中保留两个 sidecar,然后在释放所有权之前排空其剩余数据库资源。

在 Unix 上,破坏性清理会保留 SQLite 的原生排他锁,直到数据库被移除。在 Windows 上,SQLite 的打开文件句柄会阻止 unlink;清理命令会在移除前关闭其自身的探测。原生清理必须在释放进程所有权之前完成。

正常升级通过兼容性投影和历史所有者检查,与旧的状态本地锁运行时保持互斥。在释放两个 sidecar 后,破坏性清理仅移除文件系统标识仍与其拥有的目录匹配的空目录。替换目录或新所有者的文件保持完整,清理会报告中断的移除。托管更新路径在变更之前停止旧服务。早于状态本地所有权时代的二进制文件保留其现有的受支持升级停止检查。

运维说明

  • 如果端口被另一个非网关进程占用,错误相同;请释放该端口,或使用 openclaw gateway --port <port> 选择其他端口。
  • OPENCLAW_ALLOW_MULTI_GATEWAY=1 允许多个配置/运行时实例,而不是共享可变状态。每个实例仍需要唯一的 OPENCLAW_STATE_DIR。
  • 在服务管理器下,遇到上述任一错误的新网关进程会先探测现有进程的 /healthz。如果该进程健康,新进程会保留其控制权,而不是失败。在 systemd 上,它会以代码 78 退出;该单元的 RestartPreventExitStatus=78 会阻止 Restart=always 在锁或 EADDRINUSE 冲突上循环。如果现有进程始终未变为健康,健康探测重试是有时间限制的,然后启动会因上述锁错误而失败,而不是无限循环。
  • macOS 应用在启动网关之前会保留自己的轻量级 PID 守卫;上述文件锁和 socket 绑定才是实际的运行时强制机制。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw