问题描述
SpringBoot Actuator Prometheus 暴露了中一个 http 访问计数的指标:http_server_requests_seconds_count,这是一个 counter 变量,记录了 http 接口访问次数的累计值,tag 中包含了 uri 和 Exception。
- 如果 http 接口正常返回,http_server_requests_seconds_count 指标的 Exception = "None"。
- 如果 http 接口抛出异常,http_server_requests_seconds_count 指标的 Exception 为对应的异常。
于是我用 http_server_requests_seconds_count 这个指标写了个 PromQL 表达式:sum by(app, pod, uri, exception) (irate(http_server_requests_seconds_count{app=~"app1|app2|app3", exception!="None"}[2m]))。
在 Prometheus 告警系统中,使用上述表达式统计接口调用发生异常的频率,详情可参考:服务 http 接口响应异常。
但是,但是,有时候线上接口发生异常,却没有告警事件。经实验发现,在 SpringBoot 中,暴露的 http_server_requests_seconds_count 指标,只有访问了 http 端点,该变量才会被初始化,这导致了 counter 在未初始化 --> counter=1 期间,使用 irate 函数对其求变化率为,结果永远 0。因为 counter 未初始化和 counter = 1 在同一个采样周期内,irate(counter) = 0.
解决
对于正常请求,可以执行饥饿初始化,但是对于抛异常的请求,感觉没啥好的解决办法,每个 uri 都有可能抛出各种各样的异常,不可能把各种异常都初始化一遍吧。。。
但是,但是,执行饥饿初始化只能缓解 irate(counter) 速率为 0 的情况,不能根治该问题。假设 counter 指标的采样周期为 60s,irate(counter) 的统计周期为 2min。在 counter 初始化之后,立马执行了 increment 操作,初始化和自增操作发生在同一个采样周期内,irate(counter) 仍然为 0。