一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

.NET 项目集成可观测性:从架构思考到落地实践

时间:2026-08-19 12:08:50 编辑:袖梨 来源:一聚教程网

.NET 项目集成可观测性:从架构思考到落地实践需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。

这篇内容以一个真实的.NET 9项目集成可观测性栈的经历,分享从"为什么做"到"怎么落地"的完整过程。


一、为什么需要集成可观测性?

1.1 触发点:一个"慢"接口

项目中有一个业务接口,涉及多表关联、缓存查询、外部服务调用,业务联链条很长。上线后用户反馈"慢",但具体慢在哪一步?是数据库查询?是外部调用?还是序列化?靠日志很难定位,本地复现也困难。

我的诉求很简单:想上好监控,做到心里有数。

  1. 接口整体耗时多少?哪些步骤最耗时?
  2. 系统运行时的QPS、错误率、内存、CPU是什么水平?
  3. 出问题时能快速定位,而不是靠猜。

这就是我们集成可观测性的起点——不是为了"高大上",而是为了解决一个具体的问题。


二、可观测性架构设计

此次集成,用到的核心组件有4个,OpenTelemetry,Prometheus,jaeger,grafana,在这之前,日志的部分我们已经集成到了ELK,所以整个可观测性的架构设计图如下

组件作用说明
OpenTelemetry Collector数据中枢接收应用推来的 OTLP 数据,分发给后端
Prometheus指标存储存 QPS、延迟、内存等时序数据
Jaeger链路追踪存每次请求的完整调用链
Grafana可视化把Prometheus和Jaeger的数据做成图表
ElasticSearch日志存储通过Logstash将系统产生的各类日志传输存储

注意:本篇后续不过多涉及ELK相关内容,这里点出来是因为和可观测性组件产生了交集。此外,本文演示的相关中间件的部署环境都是基于Windows,大家部署时可以根据实际情况选择部署环境。

*2.1 部署架构

我这里的数据可观测组件部署情况是,只在1台中心服务器上部署了Jaeger,Prometheus和Grafana,而所有应用节点服务器都通过Otel将数据推送到中心服务器,也就是webapi的trace/metric全部汇聚到中心服务器,只提供一个面板地址,同一排查。

这套架构在我这里的场景验证是有效的,适合中小型项目。

2.2 OpenTelemetry Collector:数据中枢

Collector是整个栈的核心枢纽,应用只跟它通信,它负责把数据分发给Prometheus和Jaeger。

核心配置:

receivers:otlp:protocols:grpc:endpoint:0.0.0.0:4317# 应用通过这个端口推数据过来processors:batch:timeout:5s# 攒批超时,减少网络开销memory_limiter:limit_mib:512# 防止数据堆积导致OOMexporters:prometheus:endpoint:"0.0.0.0:8889"# 暴露给Prometheus抓取otlp/jaeger:endpoint:127.0.0.1:14250# traces推给Jaegertls:insecure:true# 内网不需要TLSservice:pipelines:metrics:receivers: [otlp]      exporters: [prometheus]    traces:receivers: [otlp]      exporters: [otlp/jaeger]

关键点:

  1. receivers 定义数据怎么进来(OTLP gRPC)
  2. exporters 定义数据往哪去(Prometheus收指标,Jaeger收traces)
  3. processors 做批量和内存保护
  4. 注意不要加 namespace 前缀,否则Grafana查询metric名会不匹配

2.3 Prometheus:指标存储

Prometheus 定时去 OTel Collector 拉数据(pull 模式)。

核心配置:

global:scrape_interval:15s# 每 15 秒抓一次scrape_configs:-job_name:"otel-collector"static_configs:-targets: ["localhost:8889"]    # Collector的Prometheus exporter端口

启动参数:

prometheus.exe --config.file=prometheus.yml --storage.tsdb.retention.time=30d
  1. retention.time=30d:数据保留 30 天,自动清理

2.4 Jaeger:链路追踪

Jaeger负责存储和查询traces,可以看到每次请求的完整调用链。

核心配置(Jaeger v2格式):

receivers:otlp:protocols:grpc:endpoint:0.0.0.0:14250# 接收 Collector 推送的 tracesexporters:jaeger_storage_exporter:# 写入存储(必须!否则UI为空)trace_storage:jaeger_storageextensions:jaeger_query:# Web UIstorage:traces:jaeger_storagejaeger_storage:# 内存存储后端backends:jaeger_storage:memory:max_traces:100000service:pipelines:traces:receivers: [otlp]      exporters: [jaeger_storage_exporter]  # 必须包含!

踩坑提醒:traces pipeline必须包含 jaeger_storage_exporter,否则Jaeger UI里Service下拉为空,这是最常见的坑。

2.5 Grafana:可视化

Grafana负责把数据做成图表,配置两个数据源即可:

datasources:-name:Prometheustype:prometheusurl:http://localhost:9090isDefault:true-name:Jaegertype:jaegerurl:http://localhost:16686

部署顺序(很重要):

1. Jaeger          ← 必须先启动(Collector 要连它的 14250)2. OTel Collector   ← 接收应用数据3. Prometheus       ← 抓取指标4. Grafana          ← 可视化

上述服务部署后控制台输出效果分别如下

  1. jaeger服务启动后控制台输出
  1. 启动Otel Collector后传输数据效果如下
  1. 启动Prometheus后控制台输出效果如下
  1. grafana启动后控制台输出

三、项目代码

3.1 和OpenTelemetry通信

.NET项目里只需要做一件事:通过OTel SDK把数据推给OTel Collector。后面的Prometheus、Jaeger、Grafana都是接收和展示,不需要应用关心。

NuGet 包:

<PackageReferenceInclude="OpenTelemetry.Extensions.Hosting"Version="1.15.3" /><PackageReferenceInclude="OpenTelemetry.Instrumentation.AspNetCore"Version="1.15.2" /><PackageReferenceInclude="OpenTelemetry.Instrumentation.Runtime"Version="1.15.1" /><PackageReferenceInclude="OpenTelemetry.Exporter.OpenTelemetryProtocol"Version="1.15.3" />

服务注册(约 30 行代码):

privatestaticvoidConfigureOpenTelemetry(this WebApplicationBuilder builder){    string otlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]        ?? "http://localhost:4317";    builder.Services.AddOpenTelemetry()        .WithTracing(tracing =>                     {                         tracing                             .AddAspNetCoreInstrumentation()          // 自动采集HTTP请求traces                             .AddSource("Declaration.*")                    // 通配自定义Span                             .ConfigureResource(r => r.AddService("Magic.Declaration.WebAPI"))                             .AddOtlpExporter(options =>                                 {                                     options.Endpoint = new Uri(otlpEndpoint);                                     options.ExportProcessorType = ExportProcessorType.Batch;                                 });                     })        .WithMetrics(metrics =>                     {                         metrics                             .AddAspNetCoreInstrumentation()          // 自动采集HTTP请求指标                             .AddRuntimeInstrumentation()             // .NET运行时指标(GC、CPU、内存)                             .ConfigureResource(r => r.AddService("Magic.Declaration.WebAPI"))                             .AddOtlpExporter(options =>                                 {                                     options.Endpoint = new Uri(otlpEndpoint);                                 });                     });}

采集信息类别:

类别指标说明
HTTP 请求QPS、延迟分布、状态码Instrumentation.AspNetCore 自动采集
.NET 运行时GC、CPU、内存、线程池Instrumentation.Runtime 自动采集

3.2 开发环境

为了保持开发环境的高效,且能看到采集到的基本数据,我们可以在开发环境配置将采集的数据输出到控制台。

if (builder.Environment.IsDevelopment()){    tracing.AddConsoleExporter();  // traces 输出到控制台}

启动应用后,控制台会直接输出每次请求的 trace 信息:

Activity.TraceId:            8d35e...Activity.SpanId:             a7f2e...Activity.DisplayName:        GET /api/order/detailActivity.Kind:               ServerActivity.Duration:           00:00:00.1234Resource associated with Activity:    service.name: MyApp.WebAPI

效果如下

开发阶段用控制台就够了,不用装任何额外组件。

3.3 可拔插设计

这套可观测性栈是锦上添花型接入,不是项目运行的必要条件:

  1. 不装 OTel Collector:应用正常跑,只是不采集数据
  2. 不装 Prometheus/Jaeger:Collector 可以只做控制台输出
  3. 环境变量不配:代码里有默认值 http://localhost:4317

对业务代码零侵入:所有配置都收敛在服务注册阶段,业务代码不需要任何改动。


四、怎么验证和分析采集到的数据

4.1 验证数据流

部署完成后,先验证数据是否流通:

# 1. 检查 Collector 是否在接收数据curl http://localhost:8888/metrics | findstr "accepted_spans"# 2. 检查 Prometheus 是否有指标curl "http://localhost:9090/api/v1/query?query=http_server_request_duration_seconds_count"# 3. 检查 Jaeger 是否有 tracescurl http://localhost:16686/api/services

4.2 Jaeger:链路追踪分析

Jaeger UI地址:http://localhost:16686

定位链条:

打开Jaeger面板后,点击某条Trace,展开看完整的调用链:

[HTTP GET /api/order/detail]  120ms  ├── [MyApp.CreateOrder]       95ms    ← 你的自定义 Span  │     ├── [FreeSql Query]      80ms   ← 数据库查询  │     └── [Redis Get]           5ms   ← 缓存读取  └── [HTTP Response]            10ms

Tips

链路追踪数据除了在Jaeger面板查看,也可以集成到Grafana,统一管理,本篇受篇幅限制不在赘述,下面分别给出Jaeger和Grafana的面板效果。

  1. jaeger面板
  1. grafana面板

常见场景:

场景操作
接口慢但不知道慢在哪搜该接口的Trace → 看哪个Span耗时最长
间歇性超时搜Duration > 1s的Trace → 对比正常和异常的差异
500 错误搜Status = ERROR的Trace → 看Logs中的异常信息

4.3 Grafana:配置Dashboard

Grafana UI地址:http://localhost:3000

第一步:添加数据源(如果用 provisioning 文件部署则自动配置)

Configuration → Data Sources → Add data source → 选 Prometheus 和 Jaeger

第二步:创建 Dashboard

推荐先建一个包含以下面板的Dashboard:

面板数据源PromQL
实时QPSPrometheussum(rate(http_server_request_duration_seconds_count[1m]))
P95延迟Prometheushistogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket[5m])) by (le))
错误率Prometheussum(rate(http_server_request_duration_seconds_count{http_response_status_code=~"5.."}[5m])) / sum(rate(http_server_request_duration_seconds_count[5m]))
内存占用Prometheusdotnet_gc_last_collection_heap_size_bytes
CPU使用率Prometheusrate(dotnet_process_cpu_time_seconds_total[1m])

第三步:按路由分组

想看每个接口的独立指标,用 by (http_route) 分组:

sum(rate(http_server_request_duration_seconds_count[5m])) by (http_route, http_request_method)

Dashboard 设计原则:

  1. 顶部放概览数字(QPS、延迟、错误率、CPU)
  2. 中间放趋势图(按时间变化)
  3. 底部放明细表(按接口分组)

Grafana效果

注意:以上统计的方法,均可在Grafana社区官网查询到,也可以在这里(grafana.com/grafana/das…


五、总结

集成可观测性的本质是让系统运行状态可见,而不是靠猜。

四个组件各司其职:

  1. OpenTelemetry Collector 做数据中枢
  2. Prometheus 存指标
  3. Jaeger 存链路
  4. Grafana 做展示

.NET 项目要做的很少:

  1. 接入 OTel SDK,配置 OTLP 导出地址
  2. 自动采集 HTTP 请求和运行时指标
  3. 需要时加自定义 Span 定位业务逻辑
  4. 开发阶段控制台就能看,生产环境按需部署

可拔插、零侵入:

  1. 不装组件不影响业务运行
  2. 配置都在服务注册阶段,业务代码无感
  3. 环境变量控制接入程度

可观测性不是一次性任务,而是持续的过程——先跑起来,遇到问题再逐步深入。


热门栏目