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

最新下载

热门教程

.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. 环境变量控制接入程度

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


热门栏目