第88章:监控与告警:Prometheus、Grafana、Micrometer、自定义指标

监控这玩意儿,说白了就是给系统装个“心电图”。

我刚带团队那会儿,总觉得代码写好了就万事大吉。直到有一次线上服务半夜挂了,用户反馈炸了锅,我们还在那翻日志找原因……嗯,从那以后,监控就成了我项目里的“标配”。

今天咱们聊聊Kotlin项目里怎么做监控。核心工具链就四个:Prometheus(存数据)、Micrometer(采集数据)、Grafana(展示数据)、再加上自定义指标(业务层面的数据)。

一句话总结:Micrometer负责把指标暴露出来,Prometheus定时来拉,Grafana画成图表。你只需要关心“我要监控什么”,剩下的交给它们。

整体架构长什么样?

先看一张图,帮你快速建立全局认知。我个人习惯把监控系统分成三层:采集层、存储层、展示层。

Kotlin项目监控架构图 采集层(Micrometer + 自定义指标) Kotlin应用 → Micrometer注册表 → 暴露 /actuator/prometheus 端点 自定义指标:订单量、支付成功率、队列积压数…… 存储层(Prometheus Server) 定时拉取(scrape)指标数据,存入TSDB时序数据库 支持告警规则(AlertManager) 展示层(Grafana) 连接Prometheus数据源,创建仪表盘 可视化:折线图、柱状图、热力图、告警面板

第一步:引入Micrometer,让Kotlin应用“开口说话”

Micrometer是啥?你可以把它理解成一个“指标适配器”。不管底层用Prometheus还是InfluxDB,Micrometer都能统一搞定。

我在项目中习惯用Spring Boot + Kotlin,加依赖特别简单:

// build.gradle.kts
implementation("io.micrometer:micrometer-registry-prometheus:1.12.0")
implementation("org.springframework.boot:spring-boot-starter-actuator")

然后配置一下暴露端点:

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

启动应用,访问 /actuator/prometheus,你就能看到一堆指标数据了。嗯,这一步基本零成本。

小提示:Micrometer默认会采集JVM指标(内存、GC、线程数)、CPU使用率、HTTP请求计数等。这些开箱即用,不用你写一行代码。

第二步:自定义指标——监控业务层面的“心跳”

系统指标(CPU、内存)只能告诉你“机器还活着”。但业务指标才能告诉你“系统是否在正常工作”。

举个例子:我曾经遇到过一个支付服务,CPU和内存都正常,但支付成功率从99%掉到了60%。如果只看系统指标,你根本发现不了问题。这就是自定义指标的价值。

在Kotlin里写自定义指标,我一般用 MeterRegistry

import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Timer
import org.springframework.stereotype.Component
import java.util.concurrent.TimeUnit

@Component
class OrderMetrics(registry: MeterRegistry) {

    // 计数器:统计订单创建总数
    val orderCreatedCounter: Counter = Counter.builder("order.created.total")
        .description("累计创建的订单数")
        .register(registry)

    // 计时器:统计订单处理耗时
    val orderProcessTimer: Timer = Timer.builder("order.process.duration")
        .description("订单处理耗时(毫秒)")
        .register(registry)

    // 仪表盘:实时记录队列积压数
    val queueBacklogGauge = registry.gauge("order.queue.backlog", 
        listOf(Tag.of("queue", "payment")), 
        0.0) { queueBacklogValue }

    // 记录订单创建
    fun recordOrderCreated() {
        orderCreatedCounter.increment()
    }

    // 记录订单处理耗时
    fun recordOrderProcess(durationMs: Long) {
        orderProcessTimer.record(durationMs, TimeUnit.MILLISECONDS)
    }

    companion object {
        @Volatile
        var queueBacklogValue: Double = 0.0
    }
}

然后在业务代码里调用:

@Service
class OrderService(private val orderMetrics: OrderMetrics) {

    fun createOrder(order: Order): Order {
        val start = System.currentTimeMillis()
        // ... 业务逻辑 ...
        orderMetrics.recordOrderCreated()
        orderMetrics.recordOrderProcess(System.currentTimeMillis() - start)
        return order
    }
}

注意:自定义指标不要滥用。我曾经见过一个项目,每个方法都埋了十几个指标,结果Prometheus拉取时直接把应用拖垮了。指标数量控制在20个以内,够用就行。

第三步:配置Prometheus,定时“拉取”数据

Prometheus是拉模式的,它会定期访问你的 /actuator/prometheus 端点,把数据存到自己的时序数据库里。

配置文件 prometheus.yml 长这样:

scrape_configs:
  - job_name: 'kotlin-app'
    scrape_interval: 15s
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']

启动Prometheus后,访问它的Web界面,在“Status → Targets”里看到你的应用状态是“UP”,就说明连上了。

核心概念:Prometheus里每个指标都是一个时间序列,由指标名 + 标签(label)唯一标识。比如 order.created.total{instance="localhost:8080", job="kotlin-app"}。标签可以用来做分组、过滤、聚合。

第四步:Grafana——把数据变成“看得懂”的图表

Prometheus的Web界面查数据还行,但做仪表盘就差远了。这时候Grafana上场。

配置步骤很简单:

  1. 启动Grafana(默认端口3000,账号密码都是admin)
  2. 添加数据源:选择Prometheus,填上地址 http://localhost:9090
  3. 创建仪表盘:添加Panel,写PromQL查询语句

几个常用的PromQL查询:

查询目的 PromQL语句
订单创建速率(每分钟) rate(order.created.total[1m])
订单处理P99耗时 histogram_quantile(0.99, rate(order.process.duration_seconds_bucket[5m]))
队列积压数 order.queue.backlog{queue="payment"}
JVM堆内存使用率 jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}

我的习惯:每个仪表盘只放6-8个Panel,太多反而看不清。我会把“系统指标”和“业务指标”分开两个仪表盘,方便不同角色查看。

第五步:告警——让系统在出问题时“主动喊你”

光有图表还不够,你得在指标异常时收到通知。Prometheus自带AlertManager,可以配置告警规则。

举个例子,当队列积压超过1000时告警:

# alert_rules.yml
groups:
  - name: kotlin-app-alerts
    rules:
      - alert: QueueBacklogTooHigh
        expr: order.queue.backlog{queue="payment"} > 1000
        for: 2m
        annotations:
          summary: "支付队列积压过高"
          description: "当前积压数:{{ $value }},请及时处理"

然后在AlertManager里配置通知渠道(邮件、钉钉、Slack等)。我个人习惯用钉钉机器人,配置简单,响应也快。

避坑指南:我曾经把告警阈值设得太敏感,结果半夜被“CPU使用率超过50%”的告警轰炸了一整晚。后来我学乖了:告警规则一定要加 for: 2m(持续2分钟才触发),避免瞬时抖动导致误报。

完整流程回顾

咱们捋一遍整个链路:

  • Kotlin应用通过Micrometer暴露指标 →
  • Prometheus定时拉取并存储 →
  • Grafana从Prometheus读取数据,展示图表 →
  • AlertManager根据规则触发告警,通知你

你想想看,这套体系搭好之后,你早上到公司第一件事就是打开Grafana看一眼仪表盘。一切正常,安心写代码;有异常,告警早就发到你手机上了。

嗯,这就是监控该有的样子。

一句话记住:Micrometer是“嘴”,Prometheus是“大脑”,Grafana是“脸”,AlertManager是“嗓子”。四者配合,你的Kotlin项目才算真正“活”了起来。


公众号:蓝海资料掘金营,微信deep3321