尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Envoy TCP 代理下游 RST 传播到上游:`propagate_downstream_rst_to_upstream` 机制解析

Envoy TCP 代理下游 RST 传播到上游:`propagate_downstream_rst_to_upstream` 机制解析 Envoy TCP 代理下游 RST 传播到上游propagate_downstream_rst_to_upstream机制解析【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本文深入解析 Envoy 最新引入的一项行为变更在 Linux 上当 TCP 代理TCP Proxy检测到下游连接以RemoteReset即收到 RST方式关闭时Envoy 现在会将该 RST 语义传播给上游连接使上游也以 RST 而非 FIN 收尾。文章以 tcp_proxy__propagate-downstream-rst.rst 为骨架结合 TCP 代理上游连接管理与连接池源码说明该行为的判定条件、实现路径、运行时守卫与回退方法并给出对应的单元测试证据。读者可据此评估该行为对自身上游连接生命周期、连接池复用与长连接计费的影响并掌握通过 runtime guard 平滑回退的运维手段。一、变更背景TCP 代理的关闭语义与 RST/FIN 之别TCP 连接关闭时有两种截然不同的信号FIN正常关闭表示对端已完成发送后续走四次挥手流程是优雅的半关闭/全关闭RST异常重置表示对端立即终止连接丢弃所有未确认数据不进行握手常代表协议错误、异常中断或对端主动拒绝。对于承载业务的代理将下游的哪种关闭语义传递到上游直接影响上游服务的状态机、连接池复用策略以及日志排查。Envoy 的网络连接层在 envoy/network/connection.h 中定义了五种关闭类型关闭类型语义FlushWrite先冲刷待写数据再触发LocalCloseNoFlush不冲刷待写数据缓冲后立即触发LocalCloseFlushWriteAndDelay冲刷待写数据并延迟触发LocalClose直到delayed_close_timeout到期Abort不写/不冲刷任何待写数据立即触发LocalCloseAbortReset同Abort但 Envoy 会尝试以 RST 标志关闭 socket在本次变更之前TCP 代理在下游连接断开RemoteClose时无论下游是正常 FIN 关闭还是收到 RST上游连接统一以FlushWrite尝试冲刷完剩余数据后优雅关闭处理。这意味着下游的 RST 会被软化成 FIN传递到上游上游无法感知下游异常重置这一事实。二、变更内容把下游RemoteReset以AbortReset传播到上游本次变更记录于 tcp_proxy__propagate-downstream-rst.rst的核心内容可概括为一句话在 Linux 上对于直接的 TCP 代理连接direct TCP proxy connection当检测到的下游关闭类型为RemoteReset时Envoy 现在会把下游 RST 传播到上游该行为可临时回退只需将运行时守卫envoy.reloadable_features.propagate_downstream_rst_to_upstream设为false。需要拆解的几个关键限定仅限 Linux 平台RST 发送依赖平台能力宏ENVOY_PLATFORM_ENABLE_SEND_RST非 Linux 平台不具备该能力行为不受影响仅限直接的 TCP 代理连接即非隧道non-tunneled的直连模式不涉及 HTTP CONNECT 隧道等场景仅当下游检测关闭类型为RemoteReset正常 FIN 关闭Normal或 Envoy 自身发起的重置LocalReset不触发传播。判定类型DetectedCloseTypeEnvoy 在 envoy/stream_info/stream_info.h 中定义了连接关闭的检测类型enum class DetectedCloseType { Normal, // The normal socket close from Envoys connection perspective. LocalReset, // The local reset initiated from Envoy. RemoteReset, // The peer reset detected by the connection. };Normal从 Envoy 连接视角看是正常的 socket 关闭FINLocalResetEnvoy 本地发起的重置RemoteReset对端此处即下游客户端发起、由连接层检测到的重置。本次变更关注的是RemoteReset分支。相关接口见 envoy/stream_info/stream_info.h 中的setDownstreamDetectedCloseType/downstreamDetectedCloseType()。三、源码实现TcpUpstream::onDownstreamEvent的分支逻辑实现位于 source/common/tcp_proxy/upstream.cc核心是TcpUpstream::onDownstreamEvent对RemoteClose事件的处理Tcp::ConnectionPool::ConnectionData* TcpUpstream::onDownstreamEvent(Network::ConnectionEvent event, absl::string_view details) { if (event Network::ConnectionEvent::RemoteClose) { // The close call may result in this object being deleted. Latch the // connection locally so it can be returned for potential draining. auto* conn_data upstream_conn_data_.release(); Network::ConnectionCloseType close_type Network::ConnectionCloseType::FlushWrite; absl::string_view close_reason StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamRemoteClose; if (ENVOY_PLATFORM_ENABLE_SEND_RST downstream_info_.downstreamDetectedCloseType() StreamInfo::DetectedCloseType::RemoteReset Runtime::runtimeFeatureEnabled( envoy.reloadable_features.propagate_downstream_rst_to_upstream)) { close_type Network::ConnectionCloseType::AbortReset; close_reason StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamResetClose; } conn_data-connection().close(close_type, close_reason); return conn_data; } else if (event Network::ConnectionEvent::LocalClose) { upstream_conn_data_-connection().close( Network::ConnectionCloseType::NoFlush, !details.empty() ? details : StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamLocalClose); } return nullptr; }代码逻辑非常直白可以概括为三层递进的判定默认路径关闭类型 FlushWrite下游断开时上游连接走冲刷待写数据后优雅关闭关闭原因为ClosingUpstreamTcpDueToDownstreamRemoteClose传播条件只有当以下三个条件同时成立时才切换到AbortReset平台支持发送 RSTENVOY_PLATFORM_ENABLE_SEND_RSTLinux 平台满足下游检测关闭类型为RemoteResetdownstream_info_.downstreamDetectedCloseType() StreamInfo::DetectedCloseType::RemoteReset运行时特性开启Runtime::runtimeFeatureEnabled(envoy.reloadable_features.propagate_downstream_rst_to_upstream)传播路径关闭类型 AbortReset上游连接以 RST 关闭关闭原因改为ClosingUpstreamTcpDueToDownstreamResetClose。这里通过upstream_conn_data_.release()先摘下连接数据再关闭是因为close()可能触发本对象析构注释也明确说明了这一先闩存再关闭的意图。关闭原因LocalCloseReason对应关系新增的关闭原因在 envoy/stream_info/stream_info.h 中定义const std::string ClosingUpstreamTcpDueToDownstreamRemoteClose closing_upstream_tcp_connection_due_to_downstream_remote_close; const std::string ClosingUpstreamTcpDueToDownstreamLocalClose closing_upstream_tcp_connection_due_to_downstream_local_close; const std::string ClosingUpstreamTcpDueToDownstreamResetClose closing_upstream_tcp_connection_due_to_downstream_reset_close;即下游RemoteClose默认→closing_upstream_tcp_connection_due_to_downstream_remote_close下游为RemoteReset且传播生效 →closing_upstream_tcp_connection_due_to_downstream_reset_close。运维人员在访问日志、连接关闭原因中看到后者即可确认 RST 传播已生效。四、运行时守卫如何临时回退到旧行为作为可回退的行为变更该特性受 Envoy 标准 reloadable runtime feature 机制控制守卫在 source/common/runtime/runtime_features.cc 中注册RUNTIME_GUARD(envoy_reloadable_features_propagate_downstream_rst_to_upstream);回退方法若在升级后发现该行为对线上造成影响例如上游服务对 RST 过于敏感、连接池大量重建等可通过 runtime 配置将守卫临时关闭layered_runtime: layers: - name: static_layer static_layer: envoy: reloadable_features: propagate_downstream_rst_to_upstream: false在 bootstrap 配置的layered_runtime中或通过 admin 接口的/runtime动态修改 runtime layer把envoy.reloadable_features.propagate_downstream_rst_to_upstream设为false即可恢复旧的FlushWriteFIN关闭行为且无需重启 Envoy。注意该守卫是一个 reloadable可热加载特性意味着运行时可动态切换但行为变更是否对存量连接立即生效取决于连接处理路径读取 runtime 值的时机此处onDownstreamEvent每次触发都会重新读取回退只是临时手段Envoy 的行为变更最终会默认启用并移除守卫RUNTIME_GUARD默认值为 true即新行为默认开启长期运行应评估并适配新语义。五、测试验证单测如何锁定新旧两种行为该特性在 test/common/tcp_proxy/tcp_proxy_test.cc 中有对应的单元测试覆盖形成了新行为生效与守卫关闭回退的对照验证。测试一下游 RemoteReset 传播 AbortReset测试DownstreamRemoteResetPropagatesAbortReset位于 tcp_proxy_test.cc整体被#if ENVOY_PLATFORM_ENABLE_SEND_RST保护与实现中的平台条件一一对应#if ENVOY_PLATFORM_ENABLE_SEND_RST TEST_P(TcpProxyTest, DownstreamRemoteResetPropagatesAbortReset) { setup(1); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.stream_info_.setDownstreamDetectedCloseType( StreamInfo::DetectedCloseType::RemoteReset); EXPECT_CALL( *upstream_connections_.at(0), close(Network::ConnectionCloseType::AbortReset, StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamResetClose)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } #endif测试要点先通过setDownstreamDetectedCloseType(RemoteReset)显式标记下游为远端重置再触发RemoteClose事件断言上游连接以AbortResetClosingUpstreamTcpDueToDownstreamResetClose关闭。测试二守卫关闭时回退为 FIN对照测试DownstreamRemoteResetUsesFinWhenRuntimeGuardDisabled位于 tcp_proxy_test.ccTEST_P(TcpProxyTest, DownstreamRemoteResetUsesFinWhenRuntimeGuardDisabled) { scoped_runtime_.mergeValues( {{envoy.reloadable_features.propagate_downstream_rst_to_upstream, false}}); setup(1); raiseEventUpstreamConnected(0); filter_callbacks_.connection_.stream_info_.setDownstreamDetectedCloseType( StreamInfo::DetectedCloseType::RemoteReset); EXPECT_CALL( *upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamRemoteClose)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); }即使下游同样是RemoteReset一旦 runtime 守卫被设为false上游连接仍以FlushWrite 旧的ClosingUpstreamTcpDueToDownstreamRemoteClose关闭验证了回退路径的完整性与可操作性。此外同文件中的既有用例DownstreamDisconnectRemotetcp_proxy_test.cc则覆盖了未设置RemoteReset时的默认FlushWrite行为。六、适用场景与运维建议典型收益上游更早感知异常当客户端以 RST 中断如超时放弃、连接被恶意重置时上游能立即收到 RST及时释放资源而非等待 FIN 流程走完或依赖空闲超时连接池语义更准确RST 关闭的连接不会被误判为可复用连接减少了复用一个已被异常中断连接的隐患排障信号更清晰closing_upstream_tcp_connection_due_to_downstream_reset_close关闭原因让日志直接区分正常断开与下游重置。注意事项该行为仅对 Linux 平台上的直接 TCP 代理连接生效HTTP 隧道tunneled连接、非 Linux 平台不受影响升级评估时建议先在小流量/灰度环境观察上游服务的连接建立频率与错误日志变化再逐步放量若确需回退参照第四节通过layered_runtime将envoy.reloadable_features.propagate_downstream_rst_to_upstream置为false并在后续版本跟进守卫移除后的长期方案。七、总结propagate_downstream_rst_to_upstream是 Envoy 在 TCP 代理关闭语义上的重要修正它让下游 RST这一异常信号不再被代理吞掉而是以AbortReset原样传递到上游配合DetectedCloseType::RemoteReset判定与运行时守卫实现了行为变更的可控灰度与可回退。从 upstream.cc 的实现到 tcp_proxy_test.cc 的测试再到 runtime_features.cc 的守卫注册整条链路完整可追溯是理解 Envoy 连接生命周期管理与行为变更治理流程的典型示例。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表