news 2026/9/10 23:31:03

Envoy JWT AuthN 远程 JWKS 拉取 Span 采样修复:从“总是采样”到“继承父 Span 采样决策”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Envoy JWT AuthN 远程 JWKS 拉取 Span 采样修复:从“总是采样”到“继承父 Span 采样决策”

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)负责在请求进入时验证 JWT(JSON Web Token)。当 JWT 的签发方(issuer)使用远程 JWKS(JSON Web Key Set)提供公钥时,Envoy 需要通过 HTTP 从远端拉取 JWKS 来验证签名。该能力由RemoteJwks配置定义,其核心字段在 api/envoy/extensions/filters/http/jwt_authn/v3/config.proto 中说明:

  • http_uri:拉取 JWKS 的 HTTP URI,包括uriclustertimeout,必填;
  • 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()构造异步请求选项时没有设置采样字段,而异步客户端RequestOptionssampled_的默认值是true。见 envoy/http/async_client.h:

std::optional<bool> 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 span's // 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()为 false,setSampled不会被调用,子 Span 保留spawnChild时从父 Span 继承而来的采样状态。

三、采样决策如何“继承”父 Span

从源码结构看,继承链条分两层:

  1. Span 创建:async_client_impl.cc 中,当options.parent_span_非空时,通过parent_span_->spawnChild(Tracing::EgressConfig::get(), child_span_name, ...)创建子 Span。子 Span 在诞生时就带有父 Span 的采样属性(sampled 状态由父级传递)。
  2. 采样覆写:随后sampled_是否有值决定是否覆写这一继承状态。修复后 JWKS 拉取路径不再覆写,因此子 Span 的采样与否完全跟随其父 Span——通常父 Span 就是 JWT 认证过滤器所在请求的跟踪 Span,其采样由全局采样策略(如 sampling 配置、trace 决策钩子等)决定。

由此,只有在父请求本身被采样时,JWT Remote PubKey Fetch子 Span 才会被采样,从而在低采样率场景下大幅减少无意义的 JWKS 拉取跟踪数据。

四、测试验证:测试代码如何锁定这一行为

该修复在测试中被显式断言,可通过测试用例验证:

  1. 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,此测试会立即失败。
  2. JwtAuthn 集成测试:test/extensions/filters/http/jwt_authn/provider_verifier_test.cc 中同样断言了"JWT Remote PubKey Fetch"子 Span 名称,验证该 Span 在 JWT 认证主流程中的命名约定。

这些测试为“继承父 Span 采样决策”提供了可复现的行为契约,防止回归。

五、实践启示:如何在自己的过滤器/异步调用中避免“总是采样”

这条修复是一个典型的 Envoy 扩展开发范式,可推广到任何使用Http::AsyncClient::RequestOptions发起带跟踪异步请求的过滤器或组件:

  1. 显式决定采样字段:不要依赖sampled_{true}的默认值。若子 Span 应跟随请求的采样策略,请显式设置.setSampled(std::nullopt);若确需强制采样,再显式.setSampled(true)
  2. 理解继承契约spawnChild创建的子 Span 已继承父级采样状态,sampled_仅是“覆写开关”,默认的true是易被忽略的陷阱。
  3. 用测试锁定行为:参考 jwks_fetcher_test.cc 的写法,在拦截send_Invoke回调中断言options.sampled_.has_value()的取值,使采样行为成为可验证的契约。
  4. 关注改动源头:本修复记录于 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 23:30:46

Android端大模型部署实战:优化与性能调优

1. 为什么要在Android设备上部署大模型&#xff1f; 作为一名在移动端开发领域摸爬滚打多年的工程师&#xff0c;我见证了AI从云端走向终端设备的完整历程。三年前&#xff0c;当同事第一次提出"把大模型塞进手机"的想法时&#xff0c;整个团队都觉得是天方夜谭。但今…

作者头像 李华
网站建设 2026/9/10 23:29:43

高性能计算集群(HPC)构建与优化实战指南

1. 高性能计算集群的核心价值与挑战在科研机构、金融建模和AI训练等场景中&#xff0c;单台服务器往往难以满足海量数据的并行计算需求。我们曾遇到过一个典型案例&#xff1a;某生物信息团队在进行基因组测序分析时&#xff0c;单节点处理200GB样本数据需要72小时&#xff0c;…

作者头像 李华
网站建设 2026/9/10 23:29:34

Vue3组合式API+Pinia实战:从零搭建待办清单应用

最近带几个学前端的朋友做小项目&#xff0c;我发现大多数人卡住的地方往往不是单个语法点&#xff0c;而是不知道怎么把组合式 API、Pinia 状态管理、单文件组件这些零散的东西串到一个完整项目里。所以这次我挑了待办清单这个经典到不能再经典的项目&#xff0c;用 Vue3 组合…

作者头像 李华
网站建设 2026/9/10 23:28:24

四楼局域网下的命令

SSH连接笔记本和nanossh nano192.168.0.110笔记本向nano传文件scp -r /home/ccq/catkin_ws nano192.168.1.113:/home/nano# 启动 VNC&#xff0c;绑定正确的桌面环境DISPLAY:0 x11vnc -forever -shared -nopw -rfbport 5900 -listen 0.0.0.0

作者头像 李华