资讯动态

Envoy 反向隧道握手标识增强:worker_id / connection_id 的端到端关联机制

发布时间:2026/9/12 18:01:10 来源:尧图企业网站定制
Envoy 反向隧道握手标识增强worker_id / connection_id 的端到端关联机制【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本文基于 Envoy 反向隧道reverse tunnel组件在握手阶段的标识增强特性系统讲解发起端initiator如何在握手请求中携带 worker 与连接标识、接收端acceptor如何解析并在生命周期事件中暴露这些标识以及如何借助动态元数据与 filter state 在访问日志和下游过滤器中进行消费与关联。读者将掌握x-envoy-reverse-tunnel-worker-id/x-envoy-reverse-tunnel-connection-id两个握手头的完整数据流以及envoy.reverse_tunnel.initiator与envoy.reverse_tunnel.lifecycle两个动态元数据命名空间的新增字段用法能够区分并关联来自同一发起端不同 worker、不同连接的隧道实例。一、特性背景为什么要区分“同一发起端的隧道”反向隧道Reverse tunnels允许下游 Envoy 实例主动向上游 Envoy 实例建立并长期保持 TCP 连接从而让处于 NAT、防火墙或私有网络后的下游服务可以被上游侧直接访问。其基本模型是下游 Envoyinitiator发起握手连接上游 Envoyacceptor接受并缓存这些空闲连接数据请求到来时把缓存的 socket 移交给上游连接池复用。在实际部署中一个 initiator 往往运行着多个 worker 线程并且会为每个上游端点维持多条隧道连接。于是出现一个可观测性问题当上游同时收到来自同一 initiator 的多条隧道时如何区分这些隧道分别来自哪个 worker、属于哪条连接该 changelog 条目reverse_tunnel__handshake-worker-connection-id.rst描述的正是这一增强通过在握手阶段携带两个新的标识符让隧道的“出生地”信息在发起端与接收端之间端到端贯通从而能够区分并关联两条端。整个反向隧道特性的官方概览见 reverse_tunnel.rst该特性当前标注为实验性experimental且处于积极开发中。二、新增标识总览两个握手头、三个暴露出口本次增强的核心是引入两个握手 HTTP 头并打通三条消费路径维度内容握手头 1x-envoy-reverse-tunnel-worker-idinitiator worker dispatcher 名称如worker_2握手头 2x-envoy-reverse-tunnel-connection-idinitiator 侧该连接的 per-connection id出口 1initiator 访问日志envoy.reverse_tunnel.initiator动态元数据命名空间新增worker_id、connection_id字段出口 2acceptor 生命周期事件envoy.reverse_tunnel.lifecycle动态元数据命名空间新增initiator_worker_id、initiator_connection_id字段出口 3acceptor 连接 filter stateenvoy.reverse_tunnel.initiator_worker_id/envoy.reverse_tunnel.initiator_connection_id两个 filter state key其中握手头名称中的x-envoy前缀来自Http::Headers::get().prefix()即 Envoy 标准 header 前缀具体定义见 reverse_connection_utility.hreverseTunnelWorkerIdHeader()与reverseTunnelConnectionIdHeader()两个内联函数以${prefix}-reverse-tunnel-worker-id/${prefix}-reverse-tunnel-connection-id的形式构造 header 名。源码注释明确说明worker id 用于区分同一 initiator 不同 worker 发起的隧道connection id 与 worker id 组合后可区分来自同一 initiator 实例的每条独立隧道。三、发起端握手头的生成与访问日志字段3.1 握手请求中写入两个标识在 initiator 侧握手请求由RCConnectionWrapper构造。在 rc_connection_wrapper.cc 中两个标识被直接取自连接对象// Advertise which initiator worker and connection opened this tunnel so the two ends can be // correlated and tunnels from different workers/connections told apart. const Http::LowerCaseString worker_id_hdr ::Envoy::Extensions::Bootstrap::ReverseConnection::reverseTunnelWorkerIdHeader(); const Http::LowerCaseString connection_id_hdr ::Envoy::Extensions::Bootstrap::ReverseConnection::reverseTunnelConnectionIdHeader(); headers-addCopy(worker_id_hdr, connection_-dispatcher().name()); headers-addCopy(connection_id_hdr, absl::StrCat(connection_-id()));关键点connection_-dispatcher().name()即该连接所在 worker 线程 dispatcher 的名称形如worker_2、worker_3与 Envoy 线程模型中 worker 的命名一致connection_-id()是 Envoy 为该连接分配的单调递增 per-connection id以十进制字符串形式写入 header这两个 header 与既有握手头x-envoy-reverse-tunnel-node-id、x-envoy-reverse-tunnel-cluster-id、x-envoy-reverse-tunnel-tenant-id、x-envoy-reverse-tunnel-upstream-cluster-name、x-envoy-reverse-tunnel-initiation-time一起作为 HTTP 握手请求的一部分发送给 acceptor。3.2 initiator 访问日志worker_id 与 connection_idinitiator 侧每次生命周期事件handshake_success、handshake_failure、connection_closed都会触发访问日志事件上下文被写入envoy.reverse_tunnel.initiator动态元数据命名空间。在 reverse_tunnel_initiator_extension.cc 中可以看到字段填充逻辑Protobuf::Struct metadata; auto fields *metadata.mutable_fields(); fields[event].set_string_value(event); fields[node_id].set_string_value(node_id); ... fields[worker_id].set_string_value(worker_id); fields[connection_id].set_string_value(connection_id); fields[error].set_string_value(error_message); stream_info.setDynamicMetadata(envoy.reverse_tunnel.initiator, metadata);而worker_id与connection_id的实际取值来自 reverse_connection_io_handle.cc// The worker id is the worker dispatcher name (e.g. worker_2), the same identity sent in the // handshake worker-id header and used across reverse-tunnel stats. const std::string worker_id worker_dispatcher_ ! nullptr ? worker_dispatcher_-name() : std::string{}; // Render an absent connection id as an empty string; 0 is a valid id, so a sentinel would be // ambiguous. const std::string connection_id_str connection_id.has_value() ? absl::StrCat(*connection_id) : std::string{};源码注释特别指出worker id 与握手头中发送的身份完全一致且与反向隧道的 per-worker 统计使用同一身份connection id 缺席时渲染为空字符串——因为0是合法 id不能用哨兵值表示“无”。3.3 在访问日志格式中引用仓库自带的 initiator 示例配置 initiator-envoy.yaml 已经演示了如何在 stdout 访问日志中输出这两个新字段access_log: - name: envoy.access_loggers.stdout typed_config: type: - type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog log_format: text_format_source: inline_string: - [%START_TIME%] reverse_tunnel_initiator event%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:event)% node%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:node_id)% cluster%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:cluster_id)% tenant%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:tenant_id)% upstream_cluster%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:upstream_cluster)% host%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:host_address)% worker%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:worker_id)% connection_id%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:connection_id)% error%DYNAMIC_METADATA(envoy.reverse_tunnel.initiator:error)%worker_id与connection_id均为字符串类型配合既有的connection_key该隧道的连接实例唯一标识可以把handshake_success与connection_closed等事件对应到同一条隧道实例实现发起端侧的完整生命周期追踪。四、接收端解析、传播与生命周期事件暴露4.1 生命周期信息结构体acceptor 在解析握手请求后会把 initiator 的两个标识存入ReverseTunnelLifecycleInfo结构体。在 reverse_tunnel_lifecycle_info.h 中可以看到两个字段与语义注释// The acceptors own worker (dispatcher name) that owns this socket. std::string worker; // The initiators worker and connection identifiers, extracted from the handshake. Empty when the // initiator did not advertise them. Distinct from worker above, which is the acceptors. std::string initiator_worker_id; std::string initiator_connection_id;这里需要注意区分两个“worker”概念worker是 acceptor 自身负责持有该 socket 的 worker而initiator_worker_id是握手头中解析出来的、来自 initiator 的 worker 身份。当 initiator 未携带对应握手头时两个字段为空字符串。4.2 跨 worker 传播由于 acceptor 端存在 socket 负载均衡rebalance逻辑socket 可能被移交到另一个 worker 线程。两个标识在移交过程中被显式透传在 upstream_socket_manager.cc 中handoffSocketToWorker通过dispatcher_.post把initiator_worker_id/initiator_connection_id一并投递到目标 worker再由addConnectionSocket将其写入新建的ReverseTunnelLifecycleInfoReverseTunnelLifecycleInfo{.node_id node_id, ... .initiator_worker_id std::string(initiator_worker_id), .initiator_connection_id std::string(initiator_connection_id), ...};从源码结构看标识随生命周期信息以 fd 为索引保存在 socket manager 中确保无论 socket 最终落在哪个 worker标识都不会丢失。4.3 生命周期事件的元数据与 filter stateacceptor 侧的ReverseTunnelAcceptorExtension::populateLifecycleStreamInfo见 reverse_tunnel_acceptor_extension.cc在每次生命周期事件tunnel_setup、socket_handoff、tunnel_closed、idle_ping_*、http2_keepalive_timeout等上同时完成两件事写入 filter state仅当值非空且不存在时maybeSetStringFilterState(*filter_state, kFilterStateInitiatorWorkerId, lifecycle.initiator_worker_id); maybeSetStringFilterState(*filter_state, kFilterStateInitiatorConnectionId, lifecycle.initiator_connection_id);对应的 filter state key 在 reverse_tunnel_lifecycle_info.h 中定义inline constexpr absl::string_view kFilterStateInitiatorWorkerId envoy.reverse_tunnel.initiator_worker_id; inline constexpr absl::string_view kFilterStateInitiatorConnectionId envoy.reverse_tunnel.initiator_connection_id;写入动态元数据envoy.reverse_tunnel.lifecycle命名空间setStringMetadataField(metadata, initiator_worker_id, lifecycle.initiator_worker_id); setStringMetadataField(metadata, initiator_connection_id, lifecycle.initiator_connection_id);4.4 移交后post-handoff的连接同样可见握手完成、socket 被移交给上游连接池之后acceptor 上的 upstream lifecycle 网络过滤器envoy.filters.network.reverse_tunnel.upstream_lifecycle会在onNewConnection时把生命周期信息复制到移交后上游连接的 filter state 中包含node_id、cluster_id、tenant_id、worker、initiator_worker_id、initiator_connection_id与fd等见 reverse_tunnel_upstream_lifecycle.cc。这意味着即使隧道已经进入“使用中”阶段下游过滤器依然可以通过 filter state 得知这条上游连接源自 initiator 的哪个 worker 和哪条连接。五、实战跨两端关联隧道的完整方法5.1 发起端侧在 initiator 的 bootstrap extension 中配置access_log参见 initiator-envoy.yaml即可在每次握手成功/失败/关闭时输出包含worker_id、connection_id、connection_key的日志行。利用connection_keyworker_idconnection_id三元组可以精确回答“这条隧道是哪个 worker 建的、是它的第几条连接”并区分来自同一 initiator 的不同隧道。5.2 接收端侧在 acceptor 的 bootstrap extension 中配置access_log生命周期事件将携带envoy.reverse_tunnel.lifecycle元数据。新增的initiator_worker_id与initiator_connection_id字段的完整语义与官方文档 reverse_tunnel.rst 中生命周期元数据与 filter state 的字段表一致worker是 acceptor 自己的 worker而initiator_worker_id/initiator_connection_id是 initiator 在握手中通告的身份当 initiator 未通告时为空。典型关联场景多 worker 并发建连的审计initiator 开启多个 worker 时acceptor 的tunnel_setup日志会呈现initiator_worker_idworker_2、worker_3等不同值可据此判断负载分布两端事件对账initiator 侧的(worker_id, connection_id)与 acceptor 侧的(initiator_worker_id, initiator_connection_id)取自同一握手头两端的访问日志可按这两个字段对齐形成从建连handshake_success↔tunnel_setup到关闭connection_closed↔tunnel_closed的完整闭环下游过滤器消费在 reverse connection cluster 的上游连接上可通过%FILTER_STATE(envoy.reverse_tunnel.initiator_worker_id)%、%FILTER_STATE(envoy.reverse_tunnel.initiator_connection_id)%直接读取来源标识用于日志、鉴权或限流等自定义逻辑。5.3 注意事项与限制两个新字段仅在 initiator 通告时才会被 acceptor 填充对于旧版本或未启用该能力的 initiatoracceptor 侧字段为空filter state 中的对应 key 不会被设置maybeSetStringFilterState对空值直接返回initiator_worker_id的取值格式为 worker dispatcher 名称如worker_2其与反向隧道 per-worker 统计stat_prefix.worker_name.host.host.state使用同一身份可交叉验证initiator_connection_id对应 Envoy 连接对象的单调递增 id以字符串形式传输与存储0是合法 id判空应使用空字符串而非 0反向隧道整体仍为实验性特性相关握手协议与元数据字段仍在演进中升级 Envoy 版本时建议核对 changelogs 中的最新变更。六、小结本次增强以两个握手头为起点把“发起端 worker 与连接身份”这一信息沿握手、缓存、移交、使用的完整生命周期端到端贯通initiator 通过x-envoy-reverse-tunnel-worker-id/x-envoy-reverse-tunnel-connection-id通告身份并在envoy.reverse_tunnel.initiator元数据中暴露worker_id/connection_idacceptor 解析后存入ReverseTunnelLifecycleInfo随 socket 跨 worker 传播最终在envoy.reverse_tunnel.lifecycle元数据与envoy.reverse_tunnel.initiator_worker_id/envoy.reverse_tunnel.initiator_connection_idfilter state 中对外可见。由此同一 initiator 内不同 worker、不同连接建立的隧道在两端都能被精确区分与相互关联为反向隧道的排障、监控与多连接管理提供了基础标识能力。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价