Envoy QUIC 内存优化实战握手完成后重置内部 SSL 对象quic_enable_reset_ssl_after_handshake【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本指南介绍 Envoy云原生高性能边缘/中间/服务代理为 QUIC/HTTP/3 引入的一项内存优化能力在 TLS 1.3 握手完成后释放连接内部持有的 SSL 对象从而降低长连接场景下的常驻内存占用。本文将完整梳理该功能的演进背景、源码级实现调用链、三种启用方式runtime guard、默认行为与测试验证帮助你在生产环境安全地评估与开启这一优化。功能来源与变更记录该能力记录在 changelogs/current/new_features/quic__enable_reset_ssl_after_handshake.rst 中原文如下Added support for memory optimization in QUIC by resetting the internal SSL object after the handshake finishes. This can be enabled by setting the runtime guardenvoy.reloadable_features.quic_enable_reset_ssl_after_handshaketotrue.翻译为中文即为 QUIC 增加内存优化支持——在握手结束后重置内部 SSL 对象通过将运行时开关envoy.reloadable_features.quic_enable_reset_ssl_after_handshake设置为true来启用。这里的关键词是“内存优化memory optimization”与“重置内部 SSL 对象resetting the internal SSL object”。它的核心思想是QUIC 使用 TLS 1.3 完成加密握手后连接进入数据传输阶段此时 SSL 对象的大部分职责密钥协商、证书验证、会话建立已经完成继续持有该对象会白白占用内存。对于维持数分钟甚至数小时的长连接HTTP/3 的典型使用方式这一内存开销会一直持续到连接关闭。源码视角优化是如何挂接的要理解这个功能首先需要定位它在 Envoy 源码中的触发点。整个逻辑集中在 QUIC 服务端会话的初始化流程中。触发点EnvoyQuicServerSession::Initialize()在 source/common/quic/envoy_quic_server_session.cc 中EnvoyQuicServerSession::Initialize()是服务端 QUIC 会话初始化的入口void EnvoyQuicServerSession::Initialize() { if (Runtime::runtimeFeatureEnabled( envoy.reloadable_features.quic_enable_reset_ssl_after_handshake)) { enable_reset_ssl_after_handshake(); } quic::QuicServerSessionBase::Initialize(); initialized_ true; MaybeAddSessionToIdleList(); quic_connection_-setEnvoyConnection(*this, *this); }这段代码清晰地展示了设计模式首先检查 runtime guardenvoy.reloadable_features.quic_enable_reset_ssl_after_handshake是否启用Runtime::runtimeFeatureEnabled若启用则在初始化阶段调用enable_reset_ssl_after_handshake()随后继续执行基类quic::QuicServerSessionBase::Initialize()完成正常初始化。enable_reset_ssl_after_handshake()由 QUIC 实现库 QUICHE 提供声明于quiche/quic/core/http/quic_server_session_base.h该头文件在 source/common/quic/envoy_quic_server_session.h 中被引用。从命名可以推断它的作用是为会话注册一个“握手完成后重置 SSL 对象”的钩子当 TLS 握手完成时QUICHE 会释放内部 SSL 对象占用的内存。握手完成时的行为服务端会话在 source/common/quic/envoy_quic_server_session.cc 中重写了OnTlsHandshakeComplete()void EnvoyQuicServerSession::OnTlsHandshakeComplete() { quic::QuicServerSessionBase::OnTlsHandshakeComplete(); // The client certificate is already validated by EnvoyTlsServerHandshaker before this hook // runs. Surface the validated state to downstream consumers, but only when the matched chain // sets requiresClientCertificate(), so a certificate presented to a chain that does not // require one is not marked as validated. ... streamInfo().downstreamTiming().onDownstreamHandshakeComplete(dispatcher_.timeSource()); raiseConnectionEvent(Network::ConnectionEvent::Connected); }从这段注释可以看到一个关键事实客户端证书的校验由EnvoyTlsServerHandshaker在握手阶段完成握手完成后才触发OnTlsHandshakeComplete()并向上游消费者传递验证状态。这意味着“握手完成后重置 SSL 对象”是安全的——此时证书验证、ALPN/SNI 协商等握手期工作已经全部结束SSL 对象不再承担关键职责。握手信息如何在重置后继续可用一个自然的疑问是重置 SSL 对象后Enovy 需要的加密信息如协商的密码套件、ALPN、SNI、对端证书摘要等怎么办答案在 source/common/quic/quic_ssl_connection_info.h 中。QuicSslConnectionInfo是 QUIC 会话的 SSL 信息包装器它通过session_.GetCryptoStream()-GetSsl()按需访问 SSL 对象并在首次访问时缓存派生信息SSL* ssl() const override { ASSERT(session_.GetCryptoStream() ! nullptr); ASSERT(session_.GetCryptoStream()-GetSsl() ! nullptr); return session_.GetCryptoStream()-GetSsl(); }ciphersuiteId()/ciphersuiteString()从 crypto stream 直接读取alpn()/sni()首次访问时缓存为std::optionalstd::string对端证书的 SHA1/SHA256 摘要、序列号、签发者、主体、SAN 等字段通过getCachedCertificateValue()惰性计算并缓存。该头文件还特别说明了一个与本次优化直接相关的设计点QUIC SSL object doesnt cache local certs after the handshake. (QUIC 的 SSL 对象在握手后不缓存本地证书)以及QUICHE configures BoringSSL with theCRYPTO_BUFFER-based X509 method, so the base class certificate accessors abort on the QUICSSLobject. (QUICHE 使用基于CRYPTO_BUFFER的 X509 方法配置 BoringSSL因此基类的证书访问器在 QUIC 的 SSL 对象上会 abort)这说明两点其一QUIC 场景下 SSL 对象的本地证书缓存能力本就受限重置它不会损失太多缓存能力其二Envoy 通过按需解码 缓存的方式把握手阶段的关键信息提取出来使重置 SSL 对象后这些信息仍可被访问如用于访问日志、RBAC、动态元数据等下游消费。这正是该优化能够在降低内存的同时不破坏功能的基础。默认行为为什么默认是关闭的该 runtime guard 的默认值定义在 source/common/runtime/runtime_features.cc// TODO(panting): Default to true after ssl fix. FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_enable_reset_ssl_after_handshake);注意这里使用了FALSE_RUNTIME_GUARD而非RUNTIME_GUARD并且带有明确的 TODO 注释“在 ssl 修复后默认改为 true”。这意味着当前版本中该优化默认关闭需要显式开启上游维护者对该功能的态度是“先保守验证、后全面放开”——在确认所有依赖 SSL 对象的功能如访问日志中的证书信息、mTLS 场景的证书状态展示等都被妥善处理后才会翻转默认值如果你在开启后遇到与证书/SSL 信息相关的异常应关注该 TODO 所指向的修复进展。runtime_features.cc是 Envoy 所有 reloadable features 的登记表任何此类运行时开关都必须在这里注册才能被Runtime::runtimeFeatureEnabled识别。如何启用三种配置途径Envoy 的 runtime 是一个分层的“虚拟文件系统”详细机制见 docs/root/configuration/operations/runtime.rst。runtime key 中的每个.代表一个目录层级最后一个部分是文件名文件内容即 runtime 值。对应到本开关完整的文件系统路径应为symlink_root/subdirectory/reloadable_features/quic_enable_reset_ssl_after_handshake方式一静态 Bootstrap 配置LayeredRuntime在 bootstrap 配置中通过layered_runtime的static_layer直接写入适用于确定要开启、且不需要运行时动态调整的部署bootstrap: layered_runtime: layers: - name: static_layer_0 static_layer: reloadable_features: quic_enable_reset_ssl_after_handshake: true方式二本地磁盘文件DiskLayer对于需要支持动态 reload 的场景使用磁盘层。以文档中的典型配置为例layers: - name: static_layer_0 static_layer: health_check: min_interval: 5 - name: disk_layer_0 disk_layer: { symlink_root: /srv/runtime/current, subdirectory: envoy } - name: disk_layer_1 disk_layer: { symlink_root: /srv/runtime/current, subdirectory: envoy_override, append_service_cluster: true } - name: admin_layer_0 admin_layer: {}随后在磁盘上创建文件内容为true读取数值/布尔时空白与换行会被忽略/srv/runtime/current/envoy/reloadable_features/quic_enable_reset_ssl_after_handshakeEnvoy 会周期性扫描磁盘层并热加载变更无需重启进程。方式三Admin 接口AdminLayer生产环境中最灵活的方式是通过 admin 端点即时生效无需重启、无需等磁盘扫描周期POST /runtime_modify?envoy.reloadable_features.quic_enable_reset_ssl_after_handshaketrue注意 docs/root/operations/admin.rst 中的明确警告该端点立即生效且必须确保 admin 接口已被正确加固避免未授权访问。要删除通过此端点添加的覆盖传入空字符串作为 value 即可注意磁盘层加载的值只能被覆盖、不能被删除。生效时机小结途径生效时机适用场景static_layer进程启动时确定开启、无需动态调整disk_layer磁盘文件变更后的周期扫描需要可回滚的动态配置admin /runtime_modify立即生效灰度验证、故障快速回退测试验证开关确实生效该功能有专门的单元测试覆盖位于 test/common/quic/envoy_quic_server_session_test.ccTEST_F(EnvoyQuicServerSessionTestWillNotInitialize, ResetSslAfterHandshakeEnabledViaRuntime) { TestScopedRuntime scoped_runtime; scoped_runtime.mergeValues( {{envoy.reloadable_features.quic_enable_reset_ssl_after_handshake, true}}); // In this suite, envoy_quic_session_ is NOT initialized in SetUp. // So we can initialize it now, and it will pick up the runtime flag! envoy_quic_session_.Initialize(); EXPECT_TRUE(envoy_quic_session_.connection()-connected()); // The suites TearDown will handle the rest of initialization and cleanup for // envoy_quic_session_. }该测试使用了EnvoyQuicServerSessionTestWillNotInitialize这个特殊测试套件其SetUp不初始化会话从而能在测试体内先注入 runtime 值、再手动调用Initialize()验证运行时注入envoy.reloadable_features.quic_enable_reset_ssl_after_handshake true后Initialize()执行过程中会读取该开关对应生产代码中的Runtime::runtimeFeatureEnabled分支初始化完成后连接保持 connected 状态功能正常。测试与实现一一对应mergeValues模拟了 runtime 层的覆盖机制而生产代码的Initialize()就是读取该值的唯一入口。适用前提与注意事项综合以上源码与测试证据在决定是否开启该优化时请注意以下几点面向服务端连接该开关在EnvoyQuicServerSession服务端会话的初始化路径中生效主要惠及 Envoy 作为 HTTP/3 服务端、持有大量长连接的场景如边缘网关、API 网关。默认关闭、需显式开启由于FALSE_RUNTIME_GUARD与上游 TODO“ssl 修复后默认改为 true”请勿假设该行为默认存在在生产环境建议先在灰度流量上验证。与证书信息消费的关系重置 SSL 对象后对端证书信息依靠QuicSslConnectionInfo的按需解码与缓存机制继续提供若你的过滤器链/访问日志依赖 SSL 对象的某些能力应回归验证相关字段仍能正确输出。mTLS 场景客户端证书验证由EnvoyTlsServerHandshaker在握手期间完成见 source/common/quic/envoy_quic_proof_source.cc验证状态通过OnTlsHandshakeComplete()传递给下游该流程不依赖握手后的 SSL 对象存活但开启后仍建议对 mTLS 链路做针对性测试。回退手段若开启后出现异常可立即通过POST /runtime_modify?envoy.reloadable_features.quic_enable_reset_ssl_after_handshake空值删除 admin 层覆盖或直接删除磁盘层文件实现快速回退。小结envoy.reloadable_features.quic_enable_reset_ssl_after_handshake是 Envoy 针对 QUIC 长连接内存占用的一次精准优化利用“TLS 握手结束后 SSL 对象职责已完成”的特性在会话初始化时通过 runtime guard 控制是否在握手完成后重置内部 SSL 对象。其实现位于 source/common/quic/envoy_quic_server_session.cc 的Initialize()入口默认值登记于 source/common/runtime/runtime_features.cc并有 test/common/quic/envoy_quic_server_session_test.cc 的单元测试保障开关逻辑正确。对于大规模承载 HTTP/3 长连接的 Envoy 部署在充分回归证书信息消费与 mTLS 链路后开启该开关可获得可量化的常驻内存收益。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考