一、为什么需要监控DotNetCore应用

现代分布式系统中,服务就像城市里的水电管网,运行状态看不见摸不着。想象一下,如果自来水公司不知道哪段管道漏水,电力公司不清楚哪个变电站负载过高,那得多可怕?我们的DotNetCore应用也是如此。

去年我们团队就遇到过这样的情况:一个核心服务在凌晨三点突然CPU飙到99%,等早上发现时已经丢失了上万条订单数据。事后排查才发现是某个循环查询没有设置超时时间。如果有完善的监控指标,这个问题本可以在发生后的5分钟内就被发现并处理。

二、指标采集的四种武器

2.1 Application Insights的便捷之道

微软自家的Application Insights就像给应用装上了体检仪。只需要几行代码:

// 安装NuGet包:Microsoft.ApplicationInsights.AspNetCore
public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(opt => 
    {
        opt.ConnectionString = "您的InstrumentationKey";
        // 采样率设置为100%收集所有数据
        opt.RequestCollectionOptions.TrackExceptions = true; 
    });
}

这个方案最大的优点是开箱即用,连服务器内存、CPU这些基础指标都会自动收集。但要注意的是,当QPS超过5000时,采样率需要适当调整,否则账单会让人心跳加速。

2.2 Prometheus的定制化方案

对于需要深度定制的场景,Prometheus+Grafana的组合就像乐高积木。我们来看个记录HTTP请求耗时的例子:

// 安装Prometheus.Net包
var counter = Metrics.CreateCounter("http_requests_total", "HTTP请求总数");
var histogram = Metrics.CreateHistogram("http_request_duration_seconds", 
    "HTTP请求耗时分布",
    new HistogramConfiguration
    {
        Buckets = Histogram.LinearBuckets(start: 0.1, width: 0.2, count: 5)
    });

app.Use((context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    counter.Inc();
    
    try {
        return next();
    }
    finally {
        histogram.Observe(stopwatch.Elapsed.TotalSeconds);
    }
});

这种方案的灵活性极高,但需要自己维护Prometheus服务器。记得上次我们没设置好数据保留策略,结果两周就把100G的磁盘撑爆了。

2.3 OpenTelemetry的标准化尝试

OpenTelemetry正在成为监控领域的普通话。配置跨服务的追踪可以这样实现:

services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddMeter("MyAppMetrics")
        .AddOtlpExporter());

它的优势在于统一了指标、日志和追踪的采集标准。不过目前.NET的实现还不够成熟,某些高级功能可能需要自己造轮子。

2.4 自定义指标的实战技巧

有时候我们需要监控非常具体的业务指标,比如订单创建的成功率:

public class OrderMetrics
{
    private readonly Counter _createdCounter;
    private readonly Counter _failedCounter;

    public OrderMetrics()
    {
        var meter = new Meter("OrderService");
        _createdCounter = meter.CreateCounter<int>("orders_created");
        _failedCounter = meter.CreateCounter<int>("orders_failed");
    }

    public void RecordCreated() => _createdCounter.Add(1);
    public void RecordFailed() => _failedCounter.Add(1);
}

这种自定义指标最能反映业务健康度。建议把关键业务指标和系统指标分开存储,因为它们的生命周期和查询模式完全不同。

三、可视化分析的三个境界

3.1 Grafana的仪表盘艺术

Grafana就像监控数据的Photoshop。配置一个CPU监控面板的PromQL可能是这样的:

sum(rate(process_cpu_seconds_total{job="myapp"}[1m])) by (instance)

但真正厉害的是能在一个视图里关联系统指标和业务指标。比如我们发现当Redis延迟超过200ms时,订单失败率就会明显上升,这个洞察帮助我们优化了缓存策略。

3.2 时序数据库的选型建议

InfluxDB和TimescaleDB各有千秋。最近我们处理高基数指标(比如按用户ID跟踪行为)时,发现InfluxDB的索引策略更高效。但它的社区版不支持集群,这点要特别注意。

3.3 预警规则的智能设置

预警太敏感会变成"狼来了",太迟钝又会误事。我们经过多次调整,总结出这样的经验法则:

  • CPU使用率:超过80%持续5分钟告警
  • 错误率:每分钟错误数>50且错误率>1%
  • 业务指标:环比下降20%时预警

使用Prometheus的alertmanager配置示例:

groups:
- name: example
  rules:
  - alert: HighErrorRate
    expr: rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m]) > 0.01
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "高错误率发生在 {{ $labels.instance }}"

四、落地实践中的血泪教训

4.1 指标爆炸的预防措施

曾经我们给每个API接口都记录耗时指标,结果一个月后Prometheus崩溃了。现在遵循这些原则:

  • 核心接口:记录P99、P95耗时
  • 普通接口:只记录平均耗时
  • 批量操作:记录整体耗时而非单条

4.2 采样策略的平衡术

全量采集和采样之间需要权衡。我们的支付服务采用动态采样:

  • 正常情况:10%采样率
  • 错误请求:100%采集
  • 高峰期:自动降采样到5%

4.3 标签使用的注意事项

标签就像数据库索引,用得好加速查询,用不好拖垮系统。吃过亏后我们制定了标签规范:

  • 避免高基数标签(如用户ID)
  • 固定枚举值用标签(如省份、设备类型)
  • 变化值用指标值(如响应大小)

4.4 监控系统的自我监控

最讽刺的是监控系统自己挂了没人知道。现在我们给所有监控组件都设置了互相检查:

  • Prometheus监控Grafana
  • Grafana监控Alertmanager
  • 所有组件的心跳上报到另一个独立系统

五、未来演进方向

随着Service Mesh的普及,Sidecar模式的指标采集可能会成为主流。我们正在测试将OpenTelemetry Collector作为DaemonSet部署到K8s集群的方案,初步测试显示可以降低应用30%的监控开销。

另一个有趣的方向是把监控指标用于自动化扩缩容。我们实验性的一个项目会根据购物车结算成功率自动调整订单服务的副本数,这在促销期间特别有用。

最后要说的是,监控不是目的而是手段。我们团队墙上贴着这样一句话:"每个监控图表都应该对应一个可执行的应急预案"。毕竟,知道服务器CPU高但不知道该怎么办,比不知道CPU高更让人焦虑。