资讯动态

Rivet Guard 路由与重试机制解析:Actor 代理网关的乐观路由、缓存失效与 503 重试协议

发布时间:2026/9/17 10:22:34 来源:尧图企业网站定制
Rivet Guard 路由与重试机制解析Actor 代理网关的乐观路由、缓存失效与 503 重试协议【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors本文基于 Rivet 引擎内部文档 GUARD.md系统讲解 Rivet Engine 中 Guard 组件的三大核心机制基于缓存路由的乐观重试Retry Behavior、目标优先级路由逻辑Routing Logic以及 Actor 网关代理Gateway Proxying的请求流与 WebSocket 休眠Hibernation。读完本文你将理解 Guard 如何在“Actor 正常运行”的常见场景下以最低延迟完成路由又如何在 Actor 停止、迁移时通过 503 x-rivet-error重试协议优雅恢复并能对照 engine/packages/guard 与 engine/packages/guard-core 的源码验证每一处行为。一、Guard 是什么引擎的入口代理Guard 是 Rivet Engine 的 HTTP/WebSocket 入口代理。从源码看它的启动入口在 engine/packages/guard/src/lib.rsstart函数构造共享上下文后分别生成三个关键闭包——routing_fn路由决策、cache_key_fn路由缓存键、cert_resolverTLS 证书解析最终交给通用代理框架 engine/packages/guard-core/src/proxy_service.rs 运行。这意味着 Guard 的架构是“策略与执行分离”的guard-core提供通用的代理、重试、缓存、准入控制执行引擎而guard包负责“这个请求该去哪里”的具体路由策略Actor、Runner、API Public、Envoy 等。二、Guard Core 的重试行为乐观路由 缓存失效这是 GUARD.md 的核心设计重试机制使乐观路由与缓存失效成为可能。文档给出了三条设计原则带缓存路由的快路径Guard 使用缓存的路由信息避免每次请求都做昂贵的数据库查询提供低延迟的路由决策优雅的缓存失效当 Actor 被停止、销毁或迁移到其他 Runner 时缓存即成为“陈旧”状态。Guard 不主动失效缓存条目那需要复杂的协调而是让任何失败响应本身成为“缓存路由已失效”的信号重试时刷新服务发现重试尝试忽略缓存、执行全新的数据库查询以发现 Actor 的当前位置确保请求最终到达正确目的地。这套方案优化的是常见情况Actor 在运行、路由有效同时妥善处理罕见情况Actor 已迁移/停止且不牺牲性能。2.1 重试流程Retry Flow首次请求Attempt 1检查目标位置的路由缓存若缓存路由存在将请求发往缓存目标请求成功 → 向客户端返回响应请求以可重试错误失败 → 进入重试重试尝试Attempts 2-N等待指数退避延迟忽略缓存执行全新的数据库查询获取目标位置将请求发往新发现的目标请求成功 → 向客户端返回响应请求失败且未达到最大尝试次数 → 重复重试流程超过最大尝试次数 → 向客户端返回502 Bad Gateway重试配置文档声明 源码当前默认值配置项说明文档/源码现状指数退避起始间隔每次尝试翻倍100ms, 200ms, 400ms…文档以 100ms 为示例当前配置默认值为DEFAULT_PROXY_RETRY_INITIAL_INTERVAL_MS 150见 engine/packages/config/src/config/guard.rs最大尝试次数文档默认 3 次总尝试当前配置默认值为DEFAULT_PROXY_RETRY_MAX_ATTEMPTS 7由单元测试proxy_operational_defaults_preserve_existing_behavior锁定guard.rs重试触发条件TCP 连接错误或带x-rivet-error头的503 Service Unavailable见下文 2.2 的判定函数注意文档描述的是设计意图与早期参数仓库当前默认值150ms 起始间隔、7 次总尝试可通过proxy_retry_initial_interval_ms与proxy_retry_max_attempts两个配置项覆盖——两者在 engine/packages/config/src/config/guard.rs 中均要求最小值为 1并有参数校验逻辑。2.2 源码中的重试判定should_retry_request_inner重试触发条件的精确实现在 engine/packages/guard-core/src/utils.rspub(crate) fn should_retry_request_inner(status: StatusCode, headers: hyper::HeaderMap) - bool { (status StatusCode::SERVICE_UNAVAILABLE || status StatusCode::GATEWAY_TIMEOUT) headers .get(X_RIVET_ERROR) .and_then(|value| value.to_str().ok()) .and_then(|value| value.split_once(.)) .is_some_and(|(group, code)| group guard is_retryable_guard_http_error(code)) }由此可以确认文档中的两点细节状态码门槛实际代码要求响应状态为503 Service Unavailable或504 Gateway Timeout比文档描述的 503 更宽错误头契约x-rivet-error头必须存在且形如guard.code以.分隔出组名。仅当组名为guard且错误码属于可重试清单时才触发重试。可重试清单is_retryable_guard_http_error包含service_unavailable、actor_ready_timeout、actor_wake_retries_exceeded、actor_stopped_while_waiting、tunnel_request_aborted、tunnel_message_timeout、tunnel_response_closed、gateway_response_start_timeoututils.rs。另一个触发路径是TCP 连接错误在 HTTP 请求处理循环中只有err.is_connect()为真连接层错误且未达最大尝试次数时才会重试其他上游错误直接包装为UpstreamErrorproxy_service.rs。2.3 “失败即失效”的实现重试时忽略缓存文档所述“重试忽略缓存”在 proxy_service.rs 中有直接对应// Resolve target again, this time ignoring cache. This makes sure // we always re-fetch the route on error let ResolveRouteOutput::Target(new_target) self.state.resolve_route(req_ctx, true).await? else { bail!(resolved route does not match Target); }; target new_target;resolve_route的第二个参数ignore_cache为true时跳过route_cache查询、直接调用路由函数proxy_service.rs。退避延迟由calculate_backoff计算公式为initial_interval * 2^(attempt-1)utils.rs与文档描述的指数退避一致。另外值得说明的一点从当前源码结构看路由结果写缓存的代码处于注释状态TODO: Disable route caching for now, determine edge cases with gatewayproxy_service.rs即“快路径缓存”目前是保留能力而非始终启用的行为——这与 GUARD.md 描述的“缓存 失效信号”模型是同一套机制的两个阶段阅读源码时不应误解为缓存未实现。2.4 上游服务如何触发 Guard 重试按文档希望触发 Guard 重试的服务必须返回503 Service Unavailable状态码x-rivet-error: error头源码印证了该契约是双向的Guard 自身向外生成错误响应时err_into_response会在响应中写入x-rivet-error: {group}.{code}头utils.rs其中retry_attempts_exceeded重试耗尽映射为502 Bad Gateway、service_unavailable映射为503——正好对应文档中“重试耗尽返回 502”的行为。三、Guard Router 的路由优先级文档规定请求按如下优先级路由。对照 engine/packages/guard/src/routing/mod.rs 中create_routing_function的实现实际顺序为路径型路由Gateway / Runner / Envoy优先于目标头路由无目标头时兜底到 API Public全部不匹配则返回NoRoute错误映射为404 Not Found见 utils.rs 的错误映射表。3.1 基于目标的路由x-rivet-target头Actor 服务x-rivet-target: actor必需头x-rivet-actor: actor_id—— 具体 Actor 实例的 UUID可选头x-rivet-addr: address—— Actor 位置的直连地址覆盖行为路由到该具体 Actor 实例若 Actor 位于另一个数据中心则进行跨数据中心路由Runnerx-rivet-target: runner用途将 WebSocket 连接路由到 Pegboard runner 服务目标配置的 Pegboard 服务地址pegboard.lan_host:pegboard.port场景Runner 与编排系统之间的 WebSocket 通道3.2 API 路由无 target 头无x-rivet-target头时请求路由到公开 API 服务api_public.lan_host:api_public.port。对应实现是 api_public.rs 的默认分支api_public_default阶段原始请求路径在上游请求中保持不变。3.3 WebSocket 的协议头替代对 WebSocket 连接目标不通过x-rivet-target头而是通过Sec-Websocket-Protocol头传递。源码解析逻辑在 routing/mod.rs将协议列表按逗号拆分寻找rivet_target.前缀的项作为目标另有rivet_actor.、rivet_token.、rivet_skip_ready_wait等协议前缀用于传递 Actor ID、令牌与跳过就绪等待的标志。这与 GUARD.md 中“rivet_target.actor,rivet_actor.{actor_id}形式的逗号分隔点分对”的描述一致。3.4 路由阶段的超时围栏每个路由模块的派发都被phase_timeout包裹routing/mod.rs超时会生成RouteDispatchTimeout错误并计入guard_route_phase直方图指标。相关超时配置及默认值均可在 config/guard.rs 中覆盖包括配置项默认值用途route_timeout_ms60,000 ms路由解析的整体兜底超时route_dispatch_timeout_ms见配置定义各路由模块派发超时route_cache_ttl_ms6,000,000 ms10 分钟路由缓存 TTLroute_pegboard_fetch_actor_timeout_ms5,000 ms拉取 Pegboard Actor 路由状态route_pegboard_resolve_query_timeout_ms15,000 ms解析查询型 Actor 路由upstream_request_timeout_ms30,000 ms上游响应头接收超时四、Gateway 代理Actor 路径路由GUARD.md 指出GatewayGuard 的一部分是发往 Actor 的 HTTP 请求与 WebSocket 连接的代理。4.1 三种路径匹配模式路径解析的完整实现在 engine/packages/guard/src/routing/actor_path.rs支持三种模式与文档一致/gateway/{actor_id}/{...path}—— 直连/gateway/{actor_id}{token}/{...path}—— 带令牌的直连后为访问令牌parse_direct_actor_path/gateway/{name}/{...path}?rvt-namespace...rvt-method...—— 查询型路由通过 key 查找解析到具体 Actor查询型路由由rvt-前缀的查询参数驱动这些参数被 Rivet 网关路由保留并在转发前剥离Actor 永远看不到它们actor_path.rs。完整参数集定义在RvtParams结构体中参数含义约束rvt-namespace命名空间必填rvt-method方法仅支持get或getOrCreatervt-keyActor key 组件逗号分隔的字符串列表rvt-pool/rvt-runner计算池名getOrCreate时必填pool优先rvt-input创建输入仅getOrCreate允许Base64URL 编码且需通过 CBOR 校验rvt-region区域仅getOrCreate允许rvt-crash-policy崩溃策略取值restart/sleep/destroyrvt-token访问令牌可选rvt-skip-ready-wait跳过就绪等待布尔值注意get方法下传入rvt-input、rvt-region、rvt-crash-policy、rvt-runner会直接报QueryGetDisallowedParams错误actor_path.rs。路径解析还有单元测试覆盖engine/packages/guard/tests/parse_actor_path.rs。4.2 请求流Runner 协议的多路复用文档描述的请求流转链路从源码结构可以完整印证客户端 WebSocket 连接到运行在 Rivet Engine 上的 WebSocket 监听器即 GuardRivet Engine 通过runner 协议经 Runner 到 Guard 之间的 WebSocket 隧道把 HTTP 请求 / WebSocket 消息传输给 Actor 所在 Runner 的对应 WebSocketRunner 与 Guard 之间保持单条独立的 WebSocket 长连接与任何客户端 WebSocket 连接解耦这条单连接多路复用该 Runner 上所有 Actor 的请求与 WebSocket 连接——ProxyState中的in_flight_requestsscc::HashSetRequestId与InFlightPermit机制正是为这条隧道上的请求 ID 分配/回收设计的proxy_service.rs、utils.rsRunner 将请求与 WebSocket 委托给 Actor 处理Runner 通过同一条 WebSocket 以 runner 协议把 HTTP 响应 / WebSocket 消息发回 RivetRivet 将 runner 协议消息转换回 HTTP 响应与 WebSocket 消息。此外Guard 在转发前会通过proxied_request_builder剥除x-rivet-target、x-rivet-actor、x-rivet-token等内部头并追加X-Forwarded-Forutils.rs保证 Actor 侧看到的是干净的上游请求。4.3 WebSocket 休眠HibernationGUARD.md 最后一节指出Gateway 支持为 Actor 实现可休眠 WebSocket——客户端 WebSocket 连接保持打开的同时允许 Actor 进入睡眠当 WebSocket 上无流量时用量降为 0一旦有消息发送到 GatewayActor 会被自动唤醒。配套的 HIBERNATING_WS.md 给出了完整生命周期客户端经 Rivet由 Guard 管理建立到 Actor 的 WebSocket 连接Guard 检查 Actor 是否已唤醒已唤醒则跳过下一步未唤醒则向其 workflow 发送 Wake 信号使 Actor 分配到现有 Runnerserverless 场景则启动新 Runner 并分配Guard 通过 runner 协议向 Runner 发送ToClientWebSocketOpenRunner 回ToServerWebSocketOpen确认——Runner 必须设置.canHibernate true才能启用休眠连接建立后客户端消息经 Guard 代理到 Runner 并委托给 ActorActor 睡眠时Runner 以.hibernate true发送ToServerWebSocketCloseGuard 收到后开始休眠期间不做任何处理此后Actor 因其他来源被唤醒则回到第 6 步不再发ToClientWebSocketOpen客户端在休眠期发送消息则回到第 2 步客户端关闭 WebSocket 则唤醒 Actor若未运行并发送ToClientWebSocketClose。在源码侧guard-core通过is_ws_hibernate识别guard.websocket_service_hibernate错误码utils.rs与custom_serve.rs中的HibernationResult处理休眠的挂起/恢复路径休眠期间每个休眠 WebSocket 还运行一个 keepalive 循环定期向 UDB 写入活跃标记客户端关闭时清除该值Runner 收到CommandStartActor时也会携带仍在活跃的休眠请求信息。五、小结GUARD.md 描述的是一个“先快后准”的代理设计快路径靠缓存路由降低数据库压力正确性靠“失败即失效 重试时刷新服务发现”保证路由层通过x-rivet-target头、Sec-Websocket-Protocol协议项与/gateway/...路径三种信号把请求分流到 Actor、Runner 与 API Public而 Runner 协议上的单连接多路复用与可休眠 WebSocket则让 Actor 的长连接成本可以压到零。所有关键行为均可在 engine/packages/guard路由策略与 engine/packages/guard-core代理执行、重试、准入两个 crate 中逐条对照验证。【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价