如果让你统计每个接口每分钟调用次数怎么统计?
- 标签:后端、系统设计、场景题
🏛 业务架构师
先讲业务价值再说方案:接口分钟级调用量不是技术炫技,它是三件事的基础数据——容量规划(大促前评估扩容倍数)、成本分摊(内部结算谁用了多少资源)、SLA 违约判定(对外承诺可用性的依据)。数据不准,后面全是空中楼阁。
方案分层
量级小(创业期):业务代码里加拦截器,Redis 原子自增:
key = metric:{接口}:{yyyyMMddHHmm}
INCR key; EXPIRE key 120一天几十个 key,成本几乎为零,读时直接 GET。够用,别再造轮子。
规范做法(成熟期):不要侵入业务代码,直接复用观测体系:
- 网关/Nginx access log 作为唯一采集源,天然覆盖所有接口
- Prometheus Counter(
http_server_requests_total按 uri 维度打标)+rate()函数算每分钟速率,Grafana 出盘 - 数据再落库供长期趋势分析
架构师的取舍观点
- 这个需求永远优先用现成 APM/监控产品,自研统计系统的投入产出比极差,除非公司的多租户计费业务就靠这个赚钱。
- 统计口径要提前和业务方对齐:按"收到请求"还是"成功响应"计?网关层和的应用层数据天然有差(超时/重试),口径不一致的报表比没有报表更危险——我见过两个团队为"QPS 到底多少"吵一个月,根因就是采样点不同。
- 分钟粒度对容量规划足够,秒级实时大盘只有大促作战室才需要,按需建。
🏗 系统架构师
统计"每个接口每分钟调用量"这个需求,我会先问两个问题:量级多大?要实时还是准实时? 不同答案对应三档成本完全不同的架构。
方案一:应用内聚合(万级 QPS 内,最省) Filter/Interceptor 拦截 + LongAdder 按 接口路径+分钟 维度内存累加,定时任务每分钟把上一分钟数据批量刷到 Redis 或时序库,一次网络开销聚合了几万次调用。要点是用内存做一级聚合压缩上报量,这是所有监控 SDK 的通用套路(Micrometer 内部就是这么干的)。
方案二:网关/访问日志旁路(无侵入,中大规模) 在网关层把请求记录成 access log(异步 diskless appender,不打扰主流程),Filebeat 采集 → Flink 按 接口+分钟 窗口聚合 → 写 ClickHouse/Prometheus。业务代码零侵入,且天然覆盖所有服务。
方案三:现成可观测体系 Micrometer + Prometheus,http_server_requests_seconds_count 自带 per-uri 计数,rate() 一条 PromQL 出每分钟速率,Grafana 出面板。没有特殊定制需求时我不会自研,直接复用指标体系,还能顺带拿到 P99 延迟。
架构上的关键取舍:侵入式聚合精度高但占应用资源;日志旁路无损但端到端有秒到分钟延迟。我一般选指标体系打底 + 网关日志做审计级明细,两者互补。
💻 全栈开发专家
我分「自建埋点」和「网关/APM 统一统计」两条路,生产里通常是组合使用。
方案一:Micrometer + Prometheus(我项目里的标准做法)
写一个 AOP 切面,对 Controller 统一埋点:
java
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest req = ((ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes()).getRequest();
String uri = (String) req.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
metrics.counter("api.calls", "uri", uri, "method", req.getMethod())
.increment();
return pjp.proceed();
}然后 Prometheus 里一条 PromQL 直接出每分钟次数:
promql
sum by (uri) (rate(api_calls_total[1m]))关键坑:一定要用 BEST_MATCHING_PATTERN_ATTRIBUTE 拿路由模板(如 /user/{id}),直接取 request.getRequestURI() 会把每个 id 都变成独立标签,标签基数爆炸直接把 Prometheus 打挂。
方案二:网关层统计 Spring Cloud Gateway 写个 GlobalFilter,天然覆盖所有服务,埋点逻辑和上面一样,适合没有 APM 的团队起步。
方案三:纯 Redis 计数(小团队/临时需求) 拦截器里 INCR api:calls:{uri}:{yyyyMMddHHmm} 并设 5 分钟过期,定时任务或看板读。实现最简单,但聚合分析能力弱。
我的建议:QPS 统计本质是 RED 指标(Rate/Errors/Duration)里的 R,别只做 R——同一个切面里顺手记录耗时(Timer)和错误数,Grafana 一起看,后面排查接口变慢直接有数据可用。再用 SkyWalking 的话连埋点都可以省,它自动按 URI 聚合。