Envoy JWT AuthN 远程 JWKS 拉取 Span 采样修复从“总是采样”到“继承父 Span 采样决策”【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy导读本文围绕 Envoy 中 JWT 认证JWT AuthN过滤器的一条 bug 修复展开修复前JWT Remote PubKey Fetch子 Span 由于异步客户端请求选项将采样决策留在了默认值true导致远程 JWKS 拉取产生的跟踪 Span总是被采样修复后采样决策被显式置空unset使该子 Span继承父 Span 的采样决策。读完本文你将理解 Envoy 异步 HTTP 客户端RequestOptions::sampled_的默认行为、子 Span 采样决策的传递机制以及这条修复对应的源码与测试验证方式可直接迁移到自己的过滤器或异步调用代码中排查同类“Span 过度采样”问题。一、背景JWT AuthN 过滤器与远程 JWKS 拉取Envoy 的 JWT 认证过滤器jwt_authn负责在请求进入时验证 JWTJSON Web Token。当 JWT 的签发方issuer使用远程 JWKSJSON Web Key Set提供公钥时Envoy 需要通过 HTTP 从远端拉取 JWKS 来验证签名。该能力由RemoteJwks配置定义其核心字段在 api/envoy/extensions/filters/http/jwt_authn/v3/config.proto 中说明http_uri拉取 JWKS 的 HTTP URI包括uri、cluster与timeout必填cache_duration缓存 JWKS 的过期时长未指定时默认 10 分钟async_fetch是否在主线程异步预取 JWKS监听器激活前启用后各 worker 线程无需各自拉取。一个典型的远程 JWKS 配置片段来自 config.proto 的注释示例http_uri: uri: https://www.googleapis.com/oauth2/v1/certs cluster: jwt.www.googleapis.com|443 timeout: 1s拉取动作由JwksFetcherImpl执行它通过 HTTP 异步客户端Http::AsyncClient向上述 URI 发起 GET 请求其实现位于 source/extensions/filters/http/common/jwks_fetcher.cc。二、问题本质异步客户端默认“总是采样”2.1 采样决策的默认值在修复之前JwksFetcherImpl::fetch()构造异步请求选项时没有设置采样字段而异步客户端RequestOptions中sampled_的默认值是true。见 envoy/http/async_client.hstd::optionalbool sampled_{true};在 source/common/http/async_client_impl.cc 中该选项被直接应用到子 Span 上// Span gets sampled by default, as sampled_ defaults to true. // If caller overrides sampled_ with empty value, sampling status of the parent is kept. if (options.sampled_.has_value()) { child_span_-setSampled(options.sampled_.value()); }这意味着只要调用方不显式处理所有通过异步客户端创建的子 Span 都会被强制采样即使父 Span 本身并未被采样。对于每次远程 JWKS 拉取Envoy 都会无条件产生一条JWT Remote PubKey Fetch跟踪数据在采样率设置较低的生产环境中会显著放大 tracing 后端的数据量与成本并污染采样统计。2.2 修复前 JwksFetcher 的调用方式修复前source/extensions/filters/http/common/jwks_fetcher.cc 中的选项构造大致为auto options Http::AsyncClient::RequestOptions() .setTimeout(...) .setParentSpan(parent_span) // 采样字段保持默认true——导致总是被采样 .setChildSpanName(JWT Remote PubKey Fetch);2.3 修复方案显式置空采样选项本次 bug 修复的核心改动正是这一行——显式调用.setSampled(std::nullopt)将采样决策从“默认 true”改为“无显式值”从而让子 Span 继承父 Span 的采样决策auto options Http::AsyncClient::RequestOptions() .setTimeout(std::chrono::milliseconds( DurationUtil::durationToMilliseconds(remote_jwks_.http_uri().timeout()))) .setParentSpan(parent_span) // Leave sampled unset so the JWKS fetch span honors the parent spans // sampling decision instead of always being sampled. .setSampled(std::nullopt) .setChildSpanName(JWT Remote PubKey Fetch);改动依据正是 async_client_impl.cc 中注释明确的行为契约“If caller overrides sampled_ with empty value, sampling status of the parent is kept”——当sampled_为 nullopt 时has_value()为 falsesetSampled不会被调用子 Span 保留spawnChild时从父 Span 继承而来的采样状态。三、采样决策如何“继承”父 Span从源码结构看继承链条分两层Span 创建async_client_impl.cc 中当options.parent_span_非空时通过parent_span_-spawnChild(Tracing::EgressConfig::get(), child_span_name, ...)创建子 Span。子 Span 在诞生时就带有父 Span 的采样属性sampled 状态由父级传递。采样覆写随后sampled_是否有值决定是否覆写这一继承状态。修复后 JWKS 拉取路径不再覆写因此子 Span 的采样与否完全跟随其父 Span——通常父 Span 就是 JWT 认证过滤器所在请求的跟踪 Span其采样由全局采样策略如 sampling 配置、trace 决策钩子等决定。由此只有在父请求本身被采样时JWT Remote PubKey Fetch子 Span 才会被采样从而在低采样率场景下大幅减少无意义的 JWKS 拉取跟踪数据。四、测试验证测试代码如何锁定这一行为该修复在测试中被显式断言可通过测试用例验证JwksFetcher 单元测试test/extensions/filters/http/common/jwks_fetcher_test.cc 中的TestSpanPassedDown用例通过拦截异步客户端的send_调用检查传入的RequestOptions断言options.parent_span_ parent_span_父 Span 正确传入断言options.child_span_name_ JWT Remote PubKey Fetch关键断言EXPECT_FALSE(options.sampled_.has_value())——采样选项必须保持无值一旦将来有人重新引入默认采样或显式置 true此测试会立即失败。JwtAuthn 集成测试test/extensions/filters/http/jwt_authn/provider_verifier_test.cc 中同样断言了JWT Remote PubKey Fetch子 Span 名称验证该 Span 在 JWT 认证主流程中的命名约定。这些测试为“继承父 Span 采样决策”提供了可复现的行为契约防止回归。五、实践启示如何在自己的过滤器/异步调用中避免“总是采样”这条修复是一个典型的 Envoy 扩展开发范式可推广到任何使用Http::AsyncClient::RequestOptions发起带跟踪异步请求的过滤器或组件显式决定采样字段不要依赖sampled_{true}的默认值。若子 Span 应跟随请求的采样策略请显式设置.setSampled(std::nullopt)若确需强制采样再显式.setSampled(true)。理解继承契约spawnChild创建的子 Span 已继承父级采样状态sampled_仅是“覆写开关”默认的true是易被忽略的陷阱。用测试锁定行为参考 jwks_fetcher_test.cc 的写法在拦截send_的Invoke回调中断言options.sampled_.has_value()的取值使采样行为成为可验证的契约。关注改动源头本修复记录于 changelogs/current/bug_fixes/jwt_authn__jwks-fetch-span-honors-parent-sampling.rst如需排查同类问题可在该变更日志目录下检索其他与采样、跟踪相关的修复记录。结语本修复虽只改动一行却纠正了一个影响面广泛的默认行为远程 JWKS 拉取 Span 从“无条件被采样”变为“跟随父请求的采样决策”既保证了请求级链路跟踪的完整性又避免了低采样率环境下 tracing 数据的无效膨胀。理解RequestOptions::sampled_的默认值与async_client_impl的继承逻辑是 Envoy 扩展开发中正确控制跟踪开销的关键一环。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考