全链路压测标记透传落地实战:数据上下文设计与Sleuth Baggage的完整改造方案
一、方案设计两层机制解决两个问题在 Spring Cloud 微服务项目中落地标记透传需要同时解决跨线程和跨服务两个问题对应两套机制跨线程引入数据上下文AppContext对象底层使用 TransmittableThreadLocal确保压测标记在线程池复用场景下不丢失并支持在任意位置随取随用。跨服务利用 Sleuth 原生的 Span Baggage 机制在网关 Filter 中将压测标记写入 Baggage下游服务通过 Baggage 读取无需修改业务代码。两者组合的完整流程请求到达网关 → Filter 提取 Header 中的压测标记 → 写入 Span Baggage 存入 AppContext → 下游服务通过 Baggage 或 AppContext 读取标记 → 区分压测流量与正常流量。—## 二、网关 Filter标记注入的入口网关是整条链路的流量入口所有压测标记的注入都在这里完成。实现GlobalFilter接口在filter()方法中提取请求 Header 中的压测标记写入 Span BaggagejavaComponentpublic class globalTagFilter implements GlobalFilter { Autowired private Tracer tracer; Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String dunshan exchange.getRequest().getHeaders().getFirst(dunshan); if (dunshan ! null) { tracer.currentSpan().tag(dunshan, dunshan); // 写入 Span Baggage ServerHttpRequest request exchange.getRequest().mutate() .header(dunshan, dunshan).build(); exchange exchange.mutate().request(request).build(); } return chain.filter(exchange); }}tracer.currentSpan().tag(dunshan, dunshan) 将标记写入当前 Span 的 TagSleuth 会自动将其随 Trace 传递到所有下游服务。同时通过 mutate().header() 保留 Header确保下游服务也能通过 HttpServletRequest 直接读取。---## 三、Sleuth 配置开启 Baggage 透传仅有 Filter 代码还不够Sleuth 需要显式配置哪些 key 允许透传否则自定义字段会被过滤掉yamlspring: sleuth: sampler: probability: 1.0 rate: 10000 propagation: tag: whitelisted-keys: - dunshan # 允许该 key 作为 Tag 透传 enabled: true propagation-keys: - dunshan # 允许该 key 在服务间传播 zipkin: base-url: http://127.0.0.1:9411propagation-keys是关键配置声明了哪些自定义字段需要随 Trace 在服务间传播。whitelisted-keys控制哪些字段会作为 Tag 显示在 Zipkin UI 中。**所有服务的配置文件都需要加上这两项**否则中间某个服务会截断标记传递。---## 四、下游服务两种方式读取标记下游服务如订单服务可以用两种方式读取压测标记可根据场景灵活选择java// 方式一通过 Sleuth Baggage 读取BaggageField dunshan BaggageField.getByName(dunshan);String tagInfo dunshan.getValue();// 方式二通过 HttpServletRequest Header 读取String dunshan request.getHeader(dunshan);Baggage 方式不依赖 HTTP 协议适合 RPC 调用场景Header 方式更直接适合纯 HTTP 链路。实际项目中两种方式可以并存互为兜底。---## 五、数据上下文AppContext跨线程透传的核心Sleuth Baggage 解决了跨服务传递但在服务内部的异步场景线程池、MQ 消费者中Baggage 无法自动传递。这时需要引入 AppContext——一个基于 TTL 的数据上下文对象javapublic class AppContext implements Serializable { private static final TransmittableThreadLocalAppContext contextdunshan new TransmittableThreadLocal(); private String flag; public static AppContext getContext() { return contextdunshan.get(); } public static void setContext(AppContext context) { contextdunshan.set(context); } public static void removeContext() { contextdunshan.remove(); } public String getFlag() { return flag; } public void setFlag(String flag) { this.flag flag; }}在 Filter 或 Controller 中获取到 Baggage 标记后立即存入 AppContextjavaAppContext ctx new AppContext();ctx.setFlag(tagInfo);AppContext.setContext(ctx);之后在任意位置包括线程池中的异步任务都可以通过AppContext.getContext().getFlag()读取标记TTL 保证了线程池复用场景下的正确传递。---## 六、验证Zipkin 中确认标记透传改造完成后通过 JMeter 在 HTTP Header Manager 中添加压测标记如dunshan: 7DGroup执行请求后打开 Zipkinhttp://127.0.0.1:9411/zipkin查看链路详情。验证点有两个一是每个服务的 Span 详情中都应显示自定义 Tagdunshan7DGroup说明标记随 Trace 完整传递二是链路图中服务调用关系完整网关 → 会员 → 购物车 → 订单无断链。如果某个服务的 Span 中缺少 Tag通常是该服务的propagation-keys配置缺失补充配置后重启即可。---## 七、真实系统改造的最小改动原则在 demo 验证通过后对真实 mall 项目的改造遵循最小改动原则网关服务mall-gateway需要修改代码在已有的AuthGlobalFilter中添加 Tag 注入逻辑并更新application.yml配置。其他业务服务mall-member、mall-auth 等**只需修改配置文件**添加propagation-keys和whitelisted-keys两项配置无需改动业务代码。这是 Sleuth Baggage 方案相比 Header 方案的核心优势——标记传递对业务代码完全透明。需要使用 AppContext 读取标记的服务如需要区分压测流量写入影子库的服务在 Filter 或拦截器中完成 Baggage → AppContext 的转存业务层统一通过AppContext.getContext().getFlag() 读取与具体传输机制解耦。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2587062.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!