说实话,我以前也觉得QPS监控是那种“大团队才需要搞的东西”,小项目压测一下没问题直接上线就行了。直到上个月我们一个订单查询接口在下午高峰期突然雪崩,从用户开始反馈到系统完全不可用,前后也就十来分钟。事后复盘的时候发现,接口的QPS其实从前一天晚上就开始悄悄爬坡,从平时的800多一路涨到了当天上午的2000多,而CPU、内存报警全都没触发——因为那时候系统资源其实还够用,是中午流量再往上冲的时候才把服务打死的。如果当时有一个人在看QPS曲线,这个故障完全可以在用户感知之前就被拦下来。也就是从那次之后,我彻底改变了对监控的看法:CPU、内存是“快死的时候”才叫,QPS曲线才是“要出事之前”最早给你递纸条的那个。
这篇文章我会把SpringBoot下做QPS监控的完整思路写透,不只有代码,还有设计取舍、告警配置和上线后踩过的坑。如果你正在维护线上SpringBoot服务,或者准备给公司项目做一轮基础监控建设,这篇文章应该能让你少走不少弯路。
1. 一次线上宕机复盘:为什么QPS是比CPU内存更早的故障信号
1.1 那次故障我们到底看到了什么
先说说那次雪崩。订单查询接口跑在4台8C16G的机器上,Nginx负载均衡,后面是SpringBoot应用,MySQL和Redis做存储。从监控曲线回放来看,整个故障演进大概经历了这么几个阶段:
| 时间点 | 现象 | 系统状态 |
|---|---|---|
| 前一天22:00 | QPS从800缓慢爬升到1000 | CPU 20%,一切正常 |
| 当天09:00 | 流量高峰提前,QPS到1500 | CPU 35%,RT轻微上升 |
| 当天11:30 | QPS到2100,RT从40ms涨到120ms | 线程池活跃线程开始排队 |
| 当天12:15 | QPS到2500,RT飙到800ms+ | Tomcat线程池被打满,新请求开始拒绝 |
| 当天12:20 | 服务大面积超时,依赖的Redis连接池也爆了 | 雪崩,网关层开始报502 |
注意看,CPU和内存这两项,一直到11:30之前都是“看起来没事”的状态。真正最先变化的,是QPS那条曲线。它从一天前就开始异常爬坡,如果当时有基于QPS的告警,半夜就能收到通知,完全来得及扩容或者排查是不是有刷接口的脚本。
1.2 为什么系统指标报警总是“慢半拍”
原因其实很简单:**CPU和内存反映的是资源水位,而QPS反映的是入口流量。**流量先于压力到达,压力先于资源耗尽发生。这就像水库,上游来水增加(QPS)是最早的信号,水位上涨(CPU)是第二信号,堤坝决口(内存溢出、线程池拒绝)是最后信号。你要是只有“决口”才报警,那肯定是来不及的。
很多团队喜欢给CPU设80%报警、给内存设85%报警,这些当然要设,但它们只能告诉你“现在已经在堵了”,而QPS告诉你“马上要来一波大水”。两者不是替代关系,是互补关系。QPS监控的意义在于——在资源报警之前,预判系统会不会被流量打穿。
1.3 什么场景下你最需要QPS监控
我总结了一下,下面这几类服务强烈建议优先做QPS监控:
- 面向用户的Web接口,尤其是订单、支付、秒杀这类高价值高并发接口
- 对外提供API的服务,比如开放平台,别人调用方出问题也会拖垮你的网关
- 有定时任务批量调用的内部接口,曾经见过一个调度任务配置错了cron,每分钟调用几千次,把下游打挂了
- 依赖外部昂贵资源(数据库连接池、外部API额度)的服务,提前发现流量尖刺,能帮你省下真金白银
2. 先把口径搞清楚:瞬时QPS、平均QPS与滑动窗口
2.1 一个容易被忽略的问题:QPS到底怎么算
很多人觉得QPS不就是一个计数器除以时间吗?对,但“除以哪个时间”这件事,直接决定你的监控数据能不能用。
先说瞬时QPS。如果把每一秒单独拿出来算,这一秒内进来了1500个请求,那瞬时QPS就是1500。这种算法的优点是反应快,缺点就是毛刺特别大——某一秒可能因为网络抖动、GC暂停导致请求集中到达,瞬时值飚得老高,但下一秒就掉回几百了。你要是拿这个值去告警,一天能被误报炸十次。
再说平均QPS。把过去10秒的请求总数除以10,得到一个平均每秒的速率。这种算法平滑很多,能过滤掉秒级毛刺,看到的是比较稳定的流量趋势。监控系统里一般更倾向于用这种口径,Prometheus里的rate()函数就是这么个思路。
2.2 固定窗口和滑动窗口的差别
这个坑我在自研方案的时候踩过,必须单独拿出来说。
固定窗口的思路很简单:从某个整点时刻开始,每60秒算一个窗口,窗口内的请求数就是这一分钟的QPS。实现上用一个计数器加一个时间戳就搞定了。
但这个方案有个致命缺陷:**窗口边界处的流量会失真。**比如前59秒请求很少,最后1秒突然涌进来一大堆请求,下一秒又是一个新窗口从零开始。你在监控图上看,前面是一条平线,中间突然一根冲天柱,然后又掉回平线——但真实流量其实没那么极端。
滑动窗口的思路是把时间切成更小的粒度,比如每个槽位1秒,一共10个槽位,这10个槽位组成一个10秒的窗口。每过1秒,窗口往前滑动一格,丢弃最老的那格数据,加入最新一秒的数据。这样任何时刻的窗口都包含了过去10秒的完整信息,曲线平滑得多,也更能反映真实流量。
下面这张表可以看得很清楚:
| 方案 | 实现复杂度 | 毛刺程度 | 准确性 | 适用场景 |
|---|---|---|---|---|
| 固定窗口(秒级) | 低 | 高 | 一般 | 临时调试 |
| 滑动窗口(秒级槽位) | 中 | 低 | 高 | 生产监控 |
| 滑动窗口(毫秒级槽位) | 高 | 极低 | 极高 | 对突刺敏感的核心链路 |
2.3 单机QPS和集群QPS不是一个概念
还有一个口径问题必须提。你统计的QPS是单台机器的QPS,还是整个集群的QPS?如果服务部署了4台机器,单台看到的是集群的四分之一。你要做容量评估和告警,必须清楚自己看的什么。
自研方案默认统计的是单机视角——每个SpringBoot实例只知道自己这个JVM上收到了多少请求。而用Prometheus这类时序数据库,做查询的时候打个sum()就能把集群值聚合出来。这也是为什么后面我建议自研方案只用来临时排查,长期监控还是得落到Prometheus+Grafana这套体系上。
3. 轻量级自研方案:拦截器+环形滑动窗口,关键时刻能救命
3.1 什么时候需要自研,什么时候不用
直接说结论:如果服务已经接入了Actuator和Prometheus,那你只需要写几行查询语句就能拿到QPS,没必要自己造轮子。但如果你遇到下面这些情况,自研一个轻量方案反而是最快的:
- 服务比较老,还在用SpringBoot 1.x,升级Actuator栈的成本太高
- 公司没有统一监控平台,想先低成本看一眼当前的QPS大概是什么水平
- 排查线上问题时,想快速判断“是不是这个接口的流量异常”,不想去Grafana里敲了半天表达式
另外一个需要自研的场景是非HTTP出入口。比如一些MQ消费者、定时任务、RPC接口,这些没有走SpringMVC的拦截器链路,Actuator默认统计不到。手动埋点的话,自研方案更灵活。
3.2 环形数组实现滑动窗口:代码与原理
核心数据结构不复杂:一个长度为N的数组,每个元素代表1秒的TPS。时间戳取模落到数组的某个槽位,槽位记录这一秒内的请求数。查询最近N秒的QPS,就把这N个槽位的值加起来除以N。
如果这个槽位对应的秒已经过期了(比如上次写的是10秒前),读取的时候要先判断时间戳,过期就算0,或者直接清零,不然会把历史残留数据算进去。
package com.example.monitor; import java.util.concurrent.atomic.AtomicLongArray; /** * 基于环形数组的滑动窗口QPS计数器 * 槽位粒度:1秒 * 窗口大小:10秒 */ public class SlidingWindowQps { private static final int SIZE = 10; // 各槽位的请求计数 private final AtomicLongArray counts = new AtomicLongArray(SIZE); // 各槽位对应的秒级时间戳,用于判断槽位是否过期 private final AtomicLongArray windowStart = new AtomicLongArray(SIZE); public SlidingWindowQps() { long nowSecond = System.currentTimeMillis() / 1000; for (int i = 0; i < SIZE; i++) { windowStart.set(i, nowSecond); } } public void incr() { long nowSecond = System.currentTimeMillis() / 1000; int idx = (int) (nowSecond % SIZE); // 关键:如果当前槽位不是本秒,需要先重置 if (windowStart.get(idx) != nowSecond) { // 加锁是为了避免并发下误重置同一秒其他线程刚累加的值 synchronized (this) { if (windowStart.get(idx) != nowSecond) { windowStart.set(idx, nowSecond); counts.set(idx, 0); } } } counts.incrementAndGet(idx); } /** * 返回过去SIZE秒的平均QPS */ public long getQps() { long nowSecond = System.currentTimeMillis() / 1000; long total = 0; for (int i = 0; i < SIZE; i++) { long start = windowStart.get(i); if (nowSecond - start < SIZE) { total += counts.get(i); } } return total / SIZE; } /** * 返回当前这一秒的瞬时QPS */ public long getInstantQps() { long nowSecond = System.currentTimeMillis() / 1000; int idx = (int) (nowSecond % SIZE); if (windowStart.get(idx) != nowSecond) { return 0; } return counts.get(idx); } }注意几个细节。第一,AtomicLongArray保证并发累加安全,比用long[]加synchronized开销小很多,高并发下不会成为性能瓶颈。第二,重置槽位时用了双重检查加锁,这属于必要的保护,因为如果多个线程同时发现槽位过期,一个重置了然后又累加,另一个又重置,计数就丢了。第三,窗口大小选10秒是权衡结果,太小不抗毛刺,太大对流量变化不敏感,10秒适合大多数接口。
3.3 用拦截器埋点:只统计你关心的接口
有了计数器,第二步就是让每个请求都调用一下incr()。最简单的方式是写一个HandlerInterceptor,通过preHandle方法在请求进入Controller之前记录一次。
package com.example.monitor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class QpsInterceptor implements HandlerInterceptor { private final SlidingWindowQps qpsCounter = new SlidingWindowQps(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { qpsCounter.incr(); return true; } public SlidingWindowQps getQpsCounter() { return qpsCounter; } }然后注册进SpringMVC,通常要排除掉静态资源、监控自身的接口,不然统计出来的QPS会掺杂一堆乱七八糟的访问。
package com.example.config; import com.example.monitor.QpsInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private QpsInterceptor qpsInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(qpsInterceptor) .addPathPatterns("/api/**") .excludePathPatterns("/actuator/**", "/static/**", "/internal/qps"); } }3.4 提供查看接口:谁来问就给谁
数据统计好了,得有个口子能查。我这边提供一个内部专用的RestController,返回当前瞬时QPS和近10秒平均QPS。注意,这类接口在生产上必须做访问控制,不然监控接口本身就会成为被刷的入口。
package com.example.monitor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/internal/qps") public class QpsController { private final QpsInterceptor qpsInterceptor; public QpsController(QpsInterceptor qpsInterceptor) { this.qpsInterceptor = qpsInterceptor; } @GetMapping public Map<String, Long> qps() { SlidingWindowQps counter = qpsInterceptor.getQpsCounter(); Map<String, Long> result = new HashMap<>(); result.put("instantQps", counter.getInstantQps()); result.put("avgQps", counter.getQps()); return result; } }这样你在浏览器里访问/internal/qps,马上就能看到当前这个实例的QPS是高了还是低了。整个方案不依赖任何第三方组件,一个类加一个拦截器加一个Controller就齐了,非常适合应急排查。
3.5 实测效果:自研方案的性价比
我拿一个测试接口做了压测,部署机器4核8G,不搞任何花活,直接上JMeter开400线程循环压:
- 平稳流量时,瞬时QPS在320-350之间波动,平均QPS稳定在330左右
- 手动把压测线程翻到1000,瞬时QPS立刻跳到890,平均QPS在5秒内从330逐渐爬到780
- 整个压测过程,监控接口本身响应时间稳定在1-2ms,没有对业务产生可感知的性能影响
优点:接入成本极低,代码量不超过100行,无外部依赖,适合临时排查。
缺点:没有历史数据存储,只能看当下;如果想看过去1小时的曲线,还得自己搞存储;多实例没有自动聚合能力。所以它救急可以,长期监控还得上正儿八经的监控体系。
4. 长期方案:Micrometer + Prometheus + Grafana 一步到位
4.1 为什么这套组合是事实标准
如果公司已经有Prometheus,那SpringBoot应用接入监控几乎是零成本。Micrometer是SpringBoot默认的指标门面,它统一了各种监控后端(Prometheus、InfluxDB、Datadog等)的API;Prometheus负责抓取、存储、查询指标;Grafana负责把指标画成看得见的图,并承担告警配置。
我用一句话总结这套体系的价值:你写业务代码的时候顺手埋一个计数器,Prometheus帮你存一年,Grafana帮你画成曲线,条件到了还帮你打电话叫醒运维。
顺带说一句,SpringBoot 2.x开始,Actuator本身就有Micrometer的自动配置,你只是加个Prometheus的registry依赖而已,比很多人想象中简单得多。
4.2 接入步骤:依赖、配置、验证
第一步,引入依赖。如果是SpringBoot 3.x或者2.x,下面这两个就行:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>第二步,配置application.yml。有个特别容易踩的坑:SpringBoot 2.x之后,Actuator的端点默认只暴露health,你直接访问/actuator/prometheus会返回404,必须显式打开。
management: endpoints: web: exposure: include: health,info,prometheus metrics: tags: application: order-service第三步,启动应用,访问http://localhost:8080/actuator/prometheus。如果能看到一堆以http_server_requests_seconds开头或者jvm_开头的指标,说明已经通了。
curl http://localhost:8080/actuator/prometheus | head -n 20这时候其实不需要写一行业务代码,Prometheus已经能通过http_server_requests_seconds_count这个指标算出你的QPS。表达式是这样的:
sum(rate(http_server_requests_seconds_count[1m])) by (uri)rate函数算的是每秒增量速率,[1m]表示以1分钟为窗口做平均,by (uri)按接口路径分组。这一条语句,就是QPS监控的核心了。
4.3 自定义业务指标:QPS只是第一步
Actuator自带的HTTP指标已经覆盖了Web接口,但很多QPS相关的场景并不走HTTP。比如RPC调用、MQ消费者、定时任务。这类场景我建议在关键方法上手动埋点,Micrometer的API非常直观。
package com.example.monitor; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Component; @Component public class BusinessQpsMetrics { private final MeterRegistry meterRegistry; public BusinessQpsMetrics(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } /** * 记录某个业务动作的执行次数,相当于QPS的分子 */ public void recordAction(String actionName) { Counter.builder("business_action_total") .tag("action", actionName) .register(meterRegistry) .increment(); } }这样在Prometheus里,你就可以查到business_action_total这个指标的增长速率,对应的就是某个业务动作的QPS。相比自己维护一个静态Map来计数,Micrometer这套会自动把指标暴露给Prometheus抓取,历史数据、聚合、告警全部复用,省太多事。
4.4 Prometheus采集配置
Prometheus要主动来抓SpringBoot应用的指标,在prometheus.yml里加一个job:
scrape_configs: - job_name: 'order-service' metrics_path: '/actuator/prometheus' scrape_interval: 10s static_configs: - targets: ['192.168.1.10:8080'] labels: app: order-service instance: order-service-01这里有个细节要说一下。scrape_interval默认是1m,也就是Prometheus每60秒才来抓一次。如果你做的是QPS监控,1分钟的采样间隔太长,QPS曲线会变成一个个台阶,看起来非常难受。建议至少调到10秒,对于要精细分析的高频服务,5秒也可以。采样间隔越短,Grafana里的曲线越平滑,对QPS突刺的感知也越敏锐。
4.5 Grafana面板:一条表达式搞定QPS大屏
Grafana添加一个Panel,数据源选Prometheus,查询表达式填:
sum(rate(http_server_requests_seconds_count[1m])) by (uri)图表类型选时间序列,单位选reqps,一个清晰的QPS曲线就出来了。想看单机的话,把by (uri)改成by (instance, uri)就能看到每台机器分别的QPS。
如果想结合接口耗时一起看,再补一个P99的Panel:
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri))把QPS曲线和P99耗时曲线放在同一个Dashboard的上下两块,线上出问题的时候一眼就能看出是流量大了,还是接口变慢了。
5. 告警规则怎么定:既要能发现危险,又不能天天误报
5.1 为什么纯阈值告警是最容易误报的
监控接好了,数据也上屏了,接下来就是告警。很多人第一步就是写一个“QPS超过500就报警”的规则,结果上线第一天就被报警轰炸,为什么?因为QPS和业务时间强相关。订单系统的QPS,白天和晚上差三五倍很正常;工作日的上午10点和凌晨2点完全不是一个量级。用一个固定值卡所有时段,要么在高峰时段疯狂误报,要么在低峰时段该报不报。
我的建议是:固定阈值告警只适合“绝对值绝对不能超过”的场景,比如单台机器的QPS超过了你压测验证过的最有压力值,这属于系统要被打穿的信号。而日常的流量异常,更多要依赖环比、同比这种相对值判断。
5.2 可落地的告警组合拳
我挑几个生产验证过的规则思路,不一定照抄,但可以给你启发:
| 规则 | 表达式 | 说明 |
|---|---|---|
| 单机QPS超压测上限 | sum(rate(http_server_requests_seconds_count[1m])) by (instance) > 3000 | 超过压测验证的最大值,立即报警 |
| 集群QPS突增 | sum(rate(http_server_requests_seconds_count[5m])) by (app) / sum(rate(http_server_requests_seconds_count[5m] offset 1h)) by (app) > 3 | 和1小时前比,涨了3倍,大概率有异常引流或爬虫 |
| QPS掉零 | sum(rate(http_server_requests_seconds_count[1m])) by (app) < 1 | 持续1分钟没有任何请求,服务可能挂了或者被摘流量 |
第三条很多人会忽略。有些告警只会盯着“太高”,但流量突然掉零往往意味着更严重的问题——比如服务挂了、Nginx配置错了、数据库连不上导致所有请求直接失败。这类断崖式下跌,比突增更能说明故障。
告警持续时间for字段也很重要,比如for: 5m表示条件持续5分钟才触发。这样能过滤掉瞬时毛刺,比如某一秒因为GC暂停导致QPS抖动到很高,但下一秒就恢复了,这种不需要打扰人。
5.3 用Grafana Alerting直接推钉钉
Grafana 8.0之后内置的Alerting比老版好用很多,不需要额外部署Alertmanager也能玩转。配置路径是:Alerting -> Contact points,新建一个Webhook类型的联系点,URL填钉钉机器人的Webhook地址。
钉钉机器人配置很简单,在钉钉群里添加一个自定义机器人,拿到Webhook地址后,把地址填到Grafana的Contact point里。如果用关键词过滤策略,一定要在告警消息模板里加上钉钉机器人设置的安全关键词,比如“告警”两个字,不然钉钉会拒收。
然后在Alert rules里新建规则,查询表达式直接复用前面表格里的PromQL,条件设为“查询结果大于阈值”,通知策略选刚才配的钉钉联系点。Grafana支持邮件、webhook、钉钉、企业微信、飞书等一堆渠道,按公司习惯选就行。
5.4 进阶思路:动态阈值
如果你时间比较多,可以试一下基于历史数据的动态阈值。原理是用PromQL里的predict_linear函数,根据过去一段时间的变化趋势,预测未来一段时间的指标值。
predict_linear(http_server_requests_seconds_count[30m], 600) > 3000这条表达式会基于过去30分钟的增长趋势,预测10分钟后的QPS会不会超过3000。它解决的痛点在于:当QPS以一个缓慢但持续的斜率爬升时,固定阈值要等到真的超过3000才报警,而动态阈值在它刚到2500、但趋势明显要继续涨的时候就会提前报警。这种“趋势告警”特别适合流量缓慢上涨的故障场景。
当然,预测类告警的误报率相对高一些,适合做成warning级别,让值班同学留意即可,不要直接拉人起来。
6. 上线半年后,我踩过的一些坑和几条实用建议
6.1 监控线程池千万别用独立线程池
最开始为了不阻塞业务请求,我在监控埋点里用了一个单独的线程池来异步上报指标。结果有一次业务高峰期,监控线程池的任务队列堆了几十万条,内存直接增加了好几百兆,刚好把本就紧张的内存空间压爆了,最后业务进程被OOM Kill,监控反而成了事故的导火索。
后面我学到的教训是:**高频埋点路径上,能同步写完就同步写完,Micrometer的Counter.increment()本身是轻量操作,性能开销极低,没必要异步。**如果你真的要异步,一定要给线程池设置有界队列和拒绝策略,而且把监控自己的线程池也要加到监控里,不然早晚会出幺蛾子。
6.2http.server.requests和自定义指标重复计数的坑
Actuator自带的http_server_requests_seconds_count已经统计了所有HTTP请求,有些同事不知道这个,又自己在拦截器里写了一套计数器,最后Grafana上同一个接口的QPS出现了两个数,相互对不上,排查半天才发现是埋点重复了。
我的建议是:**HTTP层的QPS优先用Actuator自带的指标,不要再写第二遍。**只有那些不走HTTP的业务动作,比如MQ消费、定时任务,才需要你手动埋点。一个指标只在一处埋点,这条原则能让监控数据干净不少。
6.3 SpringBoot 3.x的版本兼容问题
升级到SpringBoot 3.x之后,有几个坑特别明显。首先是javax.servlet变成了jakarta.servlet,如果你是从2.x粘贴的老拦截器代码,导入语句不改直接编译失败。其次是Actuator的端点暴露机制基本没变,但Grafana面板里有些旧版本的Dashboard JSON导入后会报变量不匹配,需要重新选一下数据源和指标。
另外一个容易被忽略的点是SpringBoot 3.x默认使用Jakarta EE 10,有些老版本的Prometheus client库版本不兼容,启动会报NoClassDefFoundError。解决办法是统一用micrometer-registry-prometheus,不要自己额外引入simpleclient那套老库。
6.4 多实例部署时别只看单机面板
有次运维同事问我,为什么Grafana上QPS看起来才500多,但Nginx上的访问日志明明显示每秒好几千。一查才发现,Grafana面板的表达式没有加sum(),只显示了单台机器的QPS,4台机器各自都只有500多,聚合起来才是2000多。这是个非常低级但也非常容易犯的错。
看单机QPS,是为了排查是哪台机器异常;看集群QPS,是为了评估整体容量和告警。两个视图都要有,但脑子里得清楚当前看的是哪一个。
6.5 如果从零开始,我的建议顺序
如果你所在的项目还没有任何监控体系,我不建议一上来就按我第3节写的自研方案去造轮子。更合理的顺序是:
- 先加上Actuator和Prometheus依赖,把
/actuator/prometheus暴露出来,让Prometheus先抓到http_server_requests_seconds_count - 用PromQL在Grafana里拉出QPS曲线,哪怕只有一张图,能看见就是第一步
- 确认数据稳定后,再配置告警规则和钉钉通知
- 最后,如果确实有非HTTP场景需要统计,再用Micrometer手动埋点,补充自定义指标
这个顺序的好处是:每一步的价值都能立刻看到,而且不会做无用功。自研方案放在“Prometheus还没落地但今天就要看QPS”的临时场景里用,才是它最合适的位置。
监控这东西,和测试一样,本质是对系统的一种敬畏。你永远不知道下一次故障什么时候来,但你可以确保它来的时候,你已经准备好了。