-
C#网络编程之微服务监控与追踪(Prometheus、Jaeger)
第32章 微服务监控与追踪
32.1 微服务监控与追踪(Prometheus、Jaeger)
一、我踩过的监控坑:从“查日志查到吐”到“一眼定位问题”
微服务数量到20个时,我成了“日志搬运工”:用户说下单失败,我要翻订单服务、库存服务、支付服务的日志,每个服务查10分钟,半小时才找到是支付服务的数据库连接池满了;想知道哪个服务性能差,要每个服务看CPU、内存,开5个监控工具,眼睛都vb.net教程C#教程python教程SQL教程access 2010教程看花了。后来用Prometheus+Grafana做统一监控,所有服务的指标都在一个大屏上,一眼就能看到哪个服务延迟高、错误率高;用Jaeger做全链路追踪,输入订单ID就能看到整个调用链的每个步骤,哪里慢、哪里错一目了然,排查问题时间从半小时降到5分钟。这节我把这些踩坑经验揉进去,用大白话讲透监控和追踪的核心原理,结合C#实战代码逐行讲解,以及最佳实践和踩坑总结,让你的微服务既稳定又容易排查问题。
二、Prometheus+Grafana:微服务的“监控大屏”
Prometheus是开源的监控系统,核心优势是多维度数据模型、灵活的查询语言、高效的存储,适合监控微服务的性能指标(比如QPS、延迟、错误率、CPU、内存);Grafana是开源的可视化工具,把Prometheus的指标做成漂亮的图表和大屏,直观展示监控数据。
核心原理(大白话)
1.指标(Metric):服务暴露的监控数据,比如http_requests_total(HTTP请求总数)、process_cpu_seconds_total(CPU使用时间);
2.Exporter:收集指标的工具,比如服务自己暴露指标(C#用Prometheus.Client),或者用第三方Exporter(比如Node Exporter监控服务器CPU、内存);
3.Scrape:Prometheus定期从Exporter抓取指标,存储到时间序列数据库(TSDB);
4.PromQL:Prometheus的查询语言,用来查询和分析指标,比如sum(rate(http_requests_total[5m]))计算5分钟内的QPS;
5.Grafana:连接Prometheus,用PromQL查询指标,做成图表和大屏。
类比:Prometheus是“数据采集员”,定期去每个服务和服务器那里收集数据,存在“数据库”里;Grafana是“数据可视化师”,把数据库里的数据做成漂亮的报表和大屏,让你一眼就能看懂。
实战1:C#服务集成Prometheus(暴露指标)
用Prometheus.Client库在C#服务中暴露HTTP请求、延迟、CPU、内存等指标。
步骤1:安装NuGet包
bash
Install-Package Prometheus.Client
Install-Package Prometheus.Client.AspNetCore
Install-Package Prometheus.Client.HttpRequestDurations
步骤2:ASP.NET Core服务配置Prometheus
csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Prometheus.Client.AspNetCore;
using Prometheus.Client.HttpRequestDurations;
var builder = WebApplication.CreateBuilder(args);
// 1. 注册Prometheus中间件
builder.Services.AddHttpContextAccessor();
builder.Services.AddPrometheus();
// 注册HTTP请求时长中间件,自动收集HTTP请求的延迟、状态码等指标
builder.Services.AddHttpRequestDurations(options =>
{
options.IncludePath = true; // 包含请求路径作为标签
options.IncludeMethod = true; // 包含请求方法作为标签
options.IncludeStatus = true; // 包含响应状态码作为标签
});
var app = builder.Build();
// 2. 配置Prometheus指标端点(默认是/metrics)
app.UseMetricServer();
// 3. 启用HTTP请求时长中间件
app.UseHttpRequestDurations();
// 4. 自定义指标:订单创建计数器
var orderCreatedCounter = Metrics.CreateCounter("order_created_total", "Total number of created orders", new[] { "service_name" });
// 5. 模拟订单创建接口
app.MapPost("/api/orders", (HttpContext context) =>
{
// 增加订单创建计数器,标签service_name设为"order-service"
orderCreatedCounter.WithLabels("order-service").Inc();
return Results.Ok("Order created successfully");
});
// 6. 模拟库存扣减接口,故意返回500错误,测试错误率指标
app.MapPost("/api/stock/deduct", () =>
{
// 10%的概率返回500错误
if (new Random().Next(10) == 0)
{
return Results.StatusCode(500);
}
return Results.Ok("Stock deducted successfully");
});
app.Run("http://localhost:5000");
代码逐行讲解:
1.AddPrometheus:注册Prometheus的核心服务;
2.AddHttpRequestDurations:注册HTTP请求时长中间件,自动收集以下指标:
1.http_request_duration_seconds:HTTP请求的延迟;
2.http_requests_total:HTTP请求的总数;
3.http_requests_failed_total:HTTP请求的失败总数;
3.UseMetricServer:暴露Prometheus指标端点,默认是/metrics,Prometheus会从这个端点抓取指标;
4.CreateCounter:创建自定义计数器指标order_created_total,用来统计订单创建的总数,标签service_name用来区分不同的服务;
5.Inc():每次创建订单时,计数器加1;
6.模拟500错误:测试错误率指标,Prometheus会自动收集失败的请求数。
我踩过的坑:一开始没加标签service_name,多个服务的指标混在一起,无法区分哪个服务的订单创建多——指标一定要加标签,比如服务名、环境、版本,方便多维度分析!
步骤3:安装Prometheus并配置抓取
下载Prometheus:https://prometheus.io/download/,创建prometheus.yml配置文件:
yaml
global:
scrape_interval: 15s # 每15秒抓取一次指标
evaluation_interval: 15s # 每15秒评估一次告警规则
scrape_configs:
# 抓取Prometheus自身的指标
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# 抓取C#订单服务的指标
- job_name: "order-service"
static_configs:
- targets: ["localhost:5000"] # C#服务的地址和端口
metrics_path: "/metrics" # 指标端点,和服务配置的一致
# 抓取C#库存服务的指标(假设库存服务在5001端口)
- job_name: "stock-service"
static_configs:
- targets: ["localhost:5001"]
启动Prometheus:
bash
./prometheus --config.file=prometheus.yml
访问http://localhost:9090,用PromQL查询指标:
查询订单创建总数:order_created_total;
查询订单服务的QPS:sum(rate(http_requests_total[5m])) by (job);
查询库存服务的错误率:sum(rate(http_requests_failed_total[5m])) / sum(rate(http_requests_total[5m])) by (job)。
步骤4:安装Grafana并配置Dashboard
下载Grafana:https://grafana.com/grafana/download,启动后访问http://localhost:3000(默认账号密码admin/admin)。
1.添加数据源:Configuration → Data sources → Add data source → 选择Prometheus,输入Prometheus的地址http://localhost:9090,保存;
2.导入Dashboard:Create → Import → 输入Dashboard ID(比如ASP.NET Core的Dashboard ID是10427),选择Prometheus数据源,导入后就能看到C#服务的监控大屏,包含CPU、内存、QPS、延迟、错误率等指标。
拓展知识:Prometheus常用功能
1.Alertmanager:配置告警规则,比如QPS超过1000、错误率超过5%时发送邮件或钉钉告警;
2.Exporter:常用的Exporter有Node Exporter(监控服务器CPU、内存、磁盘)、MySQL Exporter(监控MySQL数据库)、Redis Exporter(监控Redis);
3.PromQL常用查询:
o计算QPS:sum(rate(http_requests_total[5m]));
o计算平均延迟:avg(rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]));
o计算错误率:sum(rate(http_requests_failed_total[5m])) / sum(rate(http_requests_total[5m]))。
三、Jaeger:微服务的“侦探”,追踪调用链
Jaeger是开源的分布式追踪系统,核心优势是全链路追踪、可视化调用链、性能分析,适合排查微服务之间的调用问题,比如哪个服务延迟高、哪个服务调用失败、调用链中的瓶颈在哪里。
核心原理(大白话)
1.Trace:一个完整的调用链,比如用户下单的整个过程:用户→订单服务→库存服务→支付服务→物流服务,这个过程就是一个Trace;
2.Span:调用链中的每个步骤,比如订单服务调用库存服务是一个Span,库存服务扣减库存是一个Span;
3.采样:不是所有调用都追踪,通过采样策略减少性能开销,比如采样率10%表示只追踪10%的调用;
4.上下文传递:通过HTTP头或GRPC元数据传递Trace ID和Span ID,让每个服务都能关联到同一个调用链。
类比:Trace是“侦探案的整个过程”,Span是“每个侦探的行动步骤”,采样是“只记录10%的案件”,上下文传递是“侦探之间的暗号,让大家知道是同一个案件”。
实战2:C#服务集成Jaeger(追踪调用链)
用OpenTelemetry库在C#服务中集成Jaeger,追踪HTTP请求和数据库调用的全链路。
步骤1:安装NuGet包
bash
Install-Package OpenTelemetry
Install-Package OpenTelemetry.Extensions.Hosting
Install-Package OpenTelemetry.Exporter.Jaeger
Install-Package OpenTelemetry.Instrumentation.AspNetCore
Install-Package OpenTelemetry.Instrumentation.EntityFrameworkCore
步骤2:ASP.NET Core服务配置Jaeger
csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Instrumentation.EntityFrameworkCore;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
// 1. 配置OpenTelemetry资源(服务名、版本、环境)
var resourceBuilder = ResourceBuilder.CreateDefault()
.AddService(
serviceName: "order-service", // 服务名,Jaeger中用来区分不同服务
serviceVersion: "1.0.0", // 服务版本
serviceInstanceId: Environment.MachineName); // 服务实例ID,区分同一服务的不同实例
// 2. 注册OpenTelemetry追踪
builder.Services.AddOpenTelemetry()
.ConfigureResource(resourceBuilder)
.WithTracing(tracing => tracing
// 启用ASP.NET Core instrumentation,自动追踪HTTP请求
.AddAspNetCoreInstrumentation(options =>
{
options.RecordException = true; // 记录异常信息
options.Filter = context => context.Request.Path != "/metrics"; // 忽略/metrics端点的追踪
})
// 启用Entity Framework Core instrumentation,自动追踪数据库调用
.AddEntityFrameworkCoreInstrumentation(options =>
{
options.SetDbStatementForText = true; // 记录SQL语句
options.RecordException = true; // 记录数据库异常
})
// 添加Jaeger exporter,把追踪数据发送到Jaeger
.AddJaegerExporter(options =>
{
options.AgentHost = "localhost"; // Jaeger Agent的地址
options.AgentPort = 6831; // Jaeger Agent的UDP端口
options.ExportProcessorType = ExportProcessorType.Batch; // 批量导出,减少性能开销
options.BatchExportProcessorOptions = new BatchExportProcessorOptions<Activity>
{
MaxQueueSize = 1024, // 队列最大大小
ScheduledDelayMilliseconds = 1000, // 每1秒导出一次
ExporterTimeoutMilliseconds = 5000, // 导出超时时间
MaxExportBatchSize = 256 // 每次导出的最大数量
};
})
// 自定义采样策略:采样率100%(开发环境),生产环境可以设为0.1(10%)
.SetSampler(new AlwaysOnSampler()));
// 3. 注册数据库上下文(模拟订单数据库)
builder.Services.AddDbContext<OrderDbContext>(options =>
options.UseSqlite("Data Source=orders.db"));
var app = builder.Build();
// 4. 模拟订单创建接口,调用库存服务和数据库
app.MapPost("/api/orders", async (OrderDbContext dbContext, IHttpClientFactory httpClientFactory) =>
{
// 创建订单并保存到数据库
var order = new Order { Id = Guid.NewGuid(), CreatedAt = DateTime.UtcNow };
dbContext.Orders.Add(order);
await dbContext.SaveChangesAsync();
// 调用库存服务扣减库存
var httpClient = httpClientFactory.CreateClient();
var response = await httpClient.PostAsync("http://localhost:5001/api/stock/deduct", null);
response.EnsureSuccessStatusCode();
return Results.Ok(new { OrderId = order.Id });
});
app.Run("http://localhost:5000");
// 模拟订单数据库上下文
public class OrderDbContext : DbContext
{
public OrderDbContext(DbContextOptions<OrderDbContext> options) : base(options) { }
public DbSet<Order> Orders { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public DateTime CreatedAt { get; set; }
}
代码逐行讲解:
1.ResourceBuilder:配置服务的元数据,Jaeger会用这些数据区分不同的服务和实例;
2.AddAspNetCoreInstrumentation:自动追踪HTTP请求,记录请求的路径、方法、状态码、延迟等信息;
3.AddEntityFrameworkCoreInstrumentation:自动追踪数据库调用,记录SQL语句、执行时间、异常等信息;
4.AddJaegerExporter:把追踪数据发送到Jaeger Agent,生产环境建议用批量导出(BatchExportProcessor)减少性能开销;
5.SetSampler:设置采样策略,开发环境用AlwaysOnSampler(100%采样),生产环境用TraceIdRatioBasedSampler(0.1)(10%采样);
6.OrderDbContext:模拟订单数据库,Entity Framework Core的调用会被自动追踪。
我踩过的坑:一开始没设置serviceName,Jaeger中所有服务都显示成“unknown service”,无法区分——必须设置serviceName,而且每个服务的serviceName要唯一!
步骤3:安装并启动Jaeger
用Docker启动Jaeger:
bash
docker run -d --name jaeger
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411
-p 5775:5775/udp
-p 6831:6831/udp
-p 6832:6832/udp
-p 5778:5778
-p 16686:16686
-p 14268:14268
-p 9411:9411
jaegertracing/all-in-one:1.49
访问http://localhost:16686,就能看到Jaeger UI,选择服务名order-service,点击Find Traces就能看到调用链,每个Span的延迟、SQL语句、HTTP请求信息都能看到,比如订单服务调用库存服务的延迟,数据库保存订单的SQL语句。
拓展知识:Jaeger常用功能
1.采样策略:
oAlwaysOnSampler:100%采样,适合开发环境;
oAlwaysOffSampler:0%采样,适合生产环境不需要追踪的场景;
oTraceIdRatioBasedSampler:按比例采样,比如0.1表示10%采样,适合生产环境;
oParentBasedSampler:根据父Span的采样决定是否采样,保证调用链的完整性;
2.链路上下文传递:默认通过HTTP头traceparent传递Trace ID和Span ID,支持W3C Trace Context标准,不同语言的服务都能兼容;
3.性能优化:生产环境用批量导出(BatchExportProcessor),采样率设为10%以下,减少Jaeger的性能开销;
4.与Prometheus集成:Jaeger可以和Prometheus集成,用PromQL查询追踪数据的指标,比如jaeger_spans_total统计Span的总数。
四、最佳实践与踩坑总结
-
监控与追踪结合使用
监控:用Prometheus+Grafana实时查看服务的性能指标,发现异常(比如QPS突增、错误率上升);
追踪:用Jaeger定位异常的原因,比如QPS突增是因为某个服务的数据库查询慢,错误率上升是因为某个服务调用失败。 -
生产环境部署最佳实践
(1)Prometheus
集群部署:生产环境用Prometheus集群,避免单点故障;
远程存储:用Thanos或Cortex做远程存储,解决Prometheus本地存储的容量问题;
指标命名规范:指标名用小写字母、下划线,比如order_created_total,标签名用小写字母、下划线,比如service_name;
避免高基数指标:不要用用户ID、订单ID作为标签,会导致指标基数过大,Prometheus性能下降。
(2)Jaeger
采样率设置:生产环境采样率设为10%以下,比如0.05(5%),减少性能开销;
批量导出:用BatchExportProcessor批量导出追踪数据,减少网络请求;
数据保留:配置Jaeger的数据保留时间,比如保留7天,避免磁盘空间不足;
与日志集成:把Trace ID和Span ID写入日志,比如在ASP.NET Core的日志中加入Trace ID,这样查日志时可以直接用Trace ID在Jaeger中找到对应的调用链。 -
踩过的坑
指标命名不规范:一开始指标名用驼峰命名(比如OrderCreatedTotal),PromQL查询时很麻烦,后来改成下划线命名(order_created_total),符合Prometheus的规范;
采样率设置不合理:生产环境一开始用100%采样,导致Jaeger的磁盘空间满了,服务性能下降,后来改成5%采样,性能恢复正常;
上下文传递错误:跨服务调用时忘记传递Trace ID,导致调用链断裂,后来用OpenTelemetry的自动上下文传递解决了这个问题;
监控指标不全:一开始只监控QPS和延迟,忽略了错误率,导致服务出错时没及时发现,后来加上错误率指标,配置了告警规则。
五、总结
Prometheus+Grafana是微服务监控的黄金组合,能实时查看服务的性能指标,发现异常;Jaeger是微服务追踪的利器,能定位异常的原因,排查调用链问题。把监控和追踪结合使用,能让你的微服务既稳定又容易排查问题。
下一节我们会学习微服务的安全治理:认证、授权、加密,保护微服务的通信安全。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49558.html










