news 2026/10/15 2:19:58

ASP.NET Core 实战:用 OTel Baggage 经 HttpClient 自动传递跨服务上下文

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ASP.NET Core 实战:用 OTel Baggage 经 HttpClient 自动传递跨服务上下文
  • 示例工程

【免费下载链接】practical-aspnetcore

Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.

项目地址:https://gitcode.com/gh_mirrors/pr/practical-aspnetcore
点击查看免费下载

本指南基于 practical-aspnetcore 仓库中的 open-telemetry-4 示例,讲解如何在 ASP.NET Core 应用中记录 OpenTelemetry(OTel)Baggage,并借助 HttpClient 探针自动以baggageHTTP 头把用户自定义属性传给下游 HTTP 请求。读完本文,你将掌握Activity.AddBaggage的写入方式、Baggage 与 Span Attribute 的区别、baggage头的自动注入与手动注入两种路径,以及如何在接收端读取并验证传递结果。

一、什么是 OTel Baggage

在 OpenTelemetry 中,Baggage是随请求一起在服务之间传播的一组键值对(key/value pairs),由 W3CBaggage 规范定义。与 Trace 中的 Span 不同:

  • Span Attribute(属性):只属于当前 Span,记录在当前服务的某个 trace 段上,不会随请求传递给下游服务(见仓库中的 open-telemetry-3 示例,它演示了如何用SetTag记录project、location属性)。
  • Baggage:绑定在请求上下文中,可以跨越进程边界,通过标准的baggageHTTP 请求头传递给下一个服务。

典型使用场景包括:把用户 ID、租户 ID、请求来源、A/B 实验分组等"业务上下文"从入口服务一路带到链路下游,供各服务做路由、限流或观测过滤,而无需在每个服务里重复解析业务参数。

在 .NET 的System.Diagnostics.Activity模型中,当前请求的上下文就是Activity.Current,我们可以直接在它上面附加 Baggage。

二、项目结构与依赖配置

示例位于仓库 projects/open-telemetry/open-telemetry-4 目录,只有三个文件:

open-telemetry-4/ ├── Program.cs # 全部逻辑 ├── Readme.md # 示例说明 └── open-telemetry-4.csproj # 项目文件

open-telemetry-4.csproj 目标框架为net10.0(仓库根目录的 global.json 将 SDK 锁定为 10.0.0 并允许rollForward: major),引用的 OTel 相关包均为1.17.0:

<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>true</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.17.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.http" Version="1.17.0" /> </ItemGroup> </Project>

与仓库中前三个 OTel 示例(open-telemetry-1、open-telemetry-2、open-telemetry-3)相比,本示例的关键差异是引入了OpenTelemetry.Instrumentation.Http(HTTP 客户端探针)——正是它负责在出站请求上自动注入baggage头。注意 NuGet 包名大小写不敏感,源码中写成的http与官方包名Http等价。

三、核心实现拆解

Program.cs 全部代码如下:

using System.Diagnostics; using OpenTelemetry.Trace; using OpenTelemetry.Resources; var builder = WebApplication.CreateBuilder(); builder.Services.AddOpenTelemetry().WithTracing(b => { b.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName)) .AddHttpClientInstrumentation(); }); builder.Services.AddHttpClient(); builder.Logging.SetMinimumLevel(LogLevel.Information); WebApplication app = builder.Build(); app.MapGet("/", async (HttpRequest request, IHttpClientFactory clientFactory) => { Activity.Current?.AddBaggage ("project", "practical-aspnetcore"); Activity.Current?.AddBaggage ("location", "Cairo"); var baggage = string.Join(",", Activity.Current.Baggage.ToDictionary(b => b.Key, b => b.Value).Select(x => x.Key + "=" + x.Value)); var client = clientFactory.CreateClient(); //client.DefaultRequestHeaders.Add("baggage", baggage); var url = request.Scheme + "://" + request.Host + "/baggage"; app.Logger.LogInformation("REQUEST URL " + url); var response = await client.GetAsync(url); return Results.Text("Check the log", "text/plain"); }); app.MapGet("/baggage", (HttpRequest request) => { var baggage = request.Headers["baggage"]; app.Logger.LogInformation($"BAGGAGE VALUES {baggage}"); return Results.Ok(); }); app.Run();

3.1 启动阶段:注册 OpenTelemetry 与 HTTP 客户端

builder.Services.AddOpenTelemetry().WithTracing(b => { b.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName)) .AddHttpClientInstrumentation(); }); builder.Services.AddHttpClient();
  • ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName):给 trace 资源打上service.name(值为当前应用名),与 open-telemetry-1 的用法一致。
  • AddHttpClientInstrumentation():启用 HTTP 客户端探针,监听出站请求并完成上下文(含 Baggage)注入。
  • builder.Services.AddHttpClient():注册IHttpClientFactory,后面通过工厂创建HttpClient。
  • builder.Logging.SetMinimumLevel(LogLevel.Information):把日志级别调到 Information,确保关键日志能在控制台看到(本示例没有配置AddConsoleExporter,因此观察点全部在应用日志上)。

3.2 写入 Baggage:AddBaggage

在根路由处理函数中,通过两行代码把用户自定义属性写入当前 Activity 的 Baggage:

Activity.Current?.AddBaggage ("project", "practical-aspnetcore"); Activity.Current?.AddBaggage ("location", "Cairo");

这里Activity.Current是当前 HTTP 请求对应的 Activity(ASP.NET Core 内建的Microsoft.AspNetCoreActivitySource 提供;从 .NET 11 起该 Source 会原生填充 OTel 语义属性,早期版本则依赖OpenTelemetry.Instrumentation.AspNetCore补充,参见 net11/native-otel-tracing 说明)。AddBaggage(key, value)以键值对形式把业务上下文挂到当前请求上下文上。

紧随其后的这行代码展示了如何把 Baggage 序列化成标准的key=value列表:

var baggage = string.Join(",", Activity.Current.Baggage.ToDictionary(b => b.Key, b => b.Value).Select(x => x.Key + "=" + x.Value));

它先把Activity.Current.Baggage(IEnumerable<KeyValuePair<string, string>>)转成字典,再拼接为project=practical-aspnetcore,location=Cairo这样的 W3C Baggage 字符串——这正是手动注入方案(下一节)需要的头值。

3.3 自动注入与手动注入两条路径

示例中真正发起下游请求的代码是:

var client = clientFactory.CreateClient(); //client.DefaultRequestHeaders.Add("baggage", baggage); var url = request.Scheme + "://" + request.Host + "/baggage"; app.Logger.LogInformation("REQUEST URL " + url); var response = await client.GetAsync(url);

这里有两条传递路径,值得重点区分:

  • 自动路径(示例实际采用):AddHttpClientInstrumentation()启用后,OpenTelemetry .NET SDK 会使用默认的传播器组合CompositeTextMapPropagator(内含TraceContextPropagator与BaggagePropagator),在出站请求上自动注入baggage头。因此上面被注释掉的手动加头代码不需要启用,下游端点也能收到 Baggage。
  • 手动路径(被注释的备选方案):client.DefaultRequestHeaders.Add("baggage", baggage);直接复用上一节拼接好的 Baggage 字符串,把baggage头手动写到HttpClient的默认请求头上。这一行注释在源码中(Program.cs),说明在开启 HTTP 客户端探针后自动注入已足够;当你不想依赖探针、或需要精细控制头值时,可取消注释并移除探针作为降级方案。

3.4 接收端读取:/baggage 端点

app.MapGet("/baggage", (HttpRequest request) => { var baggage = request.Headers["baggage"]; app.Logger.LogInformation($"BAGGAGE VALUES {baggage}"); return Results.Ok(); });

下游端点直接从请求头request.Headers["baggage"]取出 Baggage 值并写日志。整个示例形成一条完整回路:GET /写入 Baggage → 借助 HttpClient 以baggage头发给GET /baggage→ 接收端读取头并输出BAGGAGE VALUES ...。

四、运行与验证

在仓库中进入示例目录并启动(仓库根 global.json 指定 SDK 10.0.0):

dotnet run --project projects/open-telemetry/open-telemetry-4

应用默认监听本地端口(参考同仓库 open-telemetry-2 的运行输出,地址为http://localhost:5000)。随后访问入口端点:

curl http://localhost:5000/

浏览器/curl 会得到Check the log文本,真正的结果要看应用控制台日志。预期会依次出现两类日志(推断自 Program.cs 的逻辑):

info: ... REQUEST URL http://localhost:5000/baggage info: ... BAGGAGE VALUES project=practical-aspnetcore,location=Cairo

BAGGAGE VALUES一行即为验证结果:GET /中通过AddBaggage写入的project与location两个键值,已经以 W3Cbaggage头的形式传递到了/baggage端点并被成功解析。

五、Baggage 与 Span Attribute 的取舍

仓库中的 OTel 系列示例正好给出了同一组业务数据的两种记录方式:

  • open-telemetry-3:用Activity.Current?.SetTag("project", "practical-aspnetcore")写入Span Attribute,控制台导出的 Span 中出现project、location标签,但属性只留在本服务的当前 Span 内。
  • open-telemetry-4(本文):用AddBaggage写入Baggage,属性跟随baggage头跨服务传递。

实际选型建议:

  • 需要在本服务 trace 中检索/过滤、且不希望泄漏给下游 → 用 Span Attribute。
  • 需要随请求传给下游服务做决策或观测(如租户隔离、灰度标记)→ 用 Baggage。
  • Baggage 会随每个出站请求传播,应避免放入敏感信息(如令牌、个人数据),并注意控制键值数量与大小,防止头膨胀影响链路性能。
  • 若想在 Baggage 之外同时让下游能按业务维度聚合 trace,可结合 Attribute 一起记录,二者并不互斥。

六、小结

通过本示例可以确认一条完整链路:Activity.Current.AddBaggage写入业务上下文 →AddHttpClientInstrumentation按 W3C Baggage 规范自动注入baggage头 → 下游服务从请求头读取。这为 ASP.NET Core 分布式应用中"用户属性 / 租户信息等轻量上下文跨服务透传"提供了一个开箱即用的标准化方案,也为后续把 Baggage 与 OTel 的 trace、metrics 打通留出了空间。

  • 示例工程

【免费下载链接】practical-aspnetcore

Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.

项目地址:https://gitcode.com/gh_mirrors/pr/practical-aspnetcore
点击查看免费下载

相关推荐

上一篇:Micro框架GraphQL订阅重连机制:客户端自动恢复连接
下一篇:Cream项目完全指南:微软NAS与ViT技术大集合

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

SPIMArena:基于PHP的MIPS模拟器Web前端与寄存器可视化

简介&#xff1a;SPIMArena 是面向 UIUC CS 233&#xff08;计算机体系结构&#xff09;课程 SPIMBot 项目的 PHP 前端系统&#xff0c;适合正在学习 MIPS 汇编、准备完成 SPIMBot 竞赛的学生与助教使用。它把原本命令行式的机器人对战搬到网页界面&#xff0c;让参赛者能在线提…

作者头像 李华
网站建设 2026/10/15 2:12:51

Spring Boot应用监控实战:Actuator + Micrometer + Prometheus + Grafana

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 2:12:34

嵌入式GUI是库还是系统?FlyThings OS三层架构解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华