SpringBoot使用Prometheus采集自定義指標(biāo)數(shù)據(jù)的方法詳解
一、我們需要什么指標(biāo)
對于DDD、TDD等,大家比較熟悉了,但是對于MDD可能就比較陌生了。MDD是Metrics-Driven Development的縮寫,主張開發(fā)過程由指標(biāo)驅(qū)動,通過實用指標(biāo)來驅(qū)動快速、精確和細(xì)粒度的軟件迭代。MDD可使所有可以測量的東西都得到量化和優(yōu)化,進而為整個開發(fā)過程帶來可見性,幫助相關(guān)人員快速、準(zhǔn)確地作出決策,并在發(fā)生錯誤時立即發(fā)現(xiàn)問題并修復(fù)。依照MDD的理念,在需求階段就應(yīng)該考慮關(guān)鍵指標(biāo),在應(yīng)用上線后通過指標(biāo)了解現(xiàn)狀并持續(xù)優(yōu)化。有一些基于指標(biāo)的方法 論,建議大家了解一下:
Google的四大黃金指標(biāo):延遲Latency、流量Traffic、錯誤Errors、飽和度Saturation
Netflix的USE方法:使用率Utilization、飽和度Saturation、錯誤Error
WeaveCloud的RED方法:速率Rate、錯誤Errors、耗時Duration
二、在SrpingBoot中引入prometheus
SpringBoot2.x集成Prometheus非常簡單,首先引入maven依賴:
io.micrometer
micrometer-registry-prometheus
1.7.3
io.github.mweirauch
micrometer-jvm-extras
0.2.2然后,在application.properties中將prometheus的endpoint放出來。
management:
endpoints:
web:
exposure:
include: info,health,prometheus接下來就可以進行指標(biāo)埋點了,Prometheus的四種指標(biāo)類型此處不再贅述,請自行學(xué)習(xí)。一般指標(biāo)埋點代碼實現(xiàn)上有兩種形式:AOP、侵入式,建議盡量使用AOP記錄指標(biāo),對于無法使用aop的場景就只能侵入代碼了。常用的AOP方式有:
- @Aspect(通用)
- HandlerInterceptor (SpringMVC的攔截器)
- ClientHttpRequestInterceptor (RestTemplate的攔截器)
- DubboFilter (dubbo接口)
我們選擇通用的@Aspect,結(jié)合自定義指標(biāo)注解來實現(xiàn)。首先自定義指標(biāo)注解:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodMetrics {
String name() default "";
String desc() default "";
String[] tags() default {};
//是否記錄時間間隔
boolean withoutDuration() default false;
}然后是切面實現(xiàn):
@Aspect
public class PrometheusAnnotationAspect {
@Autowired
private MeterRegistry meterRegistry;
@Pointcut("@annotation(com.smac.prometheus.annotation.MethodMetrics)")
public void pointcut() {}
@Around(value = "pointcut()")
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
Method currentMethod = ClassUtils.getUserClass(joinPoint.getTarget().getClass()).getDeclaredMethod(targetMethod.getName(), targetMethod.getParameterTypes());
if (currentMethod.isAnnotationPresent(MethodMetrics.class)) {
MethodMetrics methodMetrics = currentMethod.getAnnotation(MethodMetrics.class);
return processMetric(joinPoint, currentMethod, methodMetrics);
} else {
return joinPoint.proceed();
}
}
private Object processMetric(ProceedingJoinPoint joinPoint, Method currentMethod, MethodMetrics methodMetrics) {
String name = methodMetrics.name();
if (!StringUtils.hasText(name)) {
name = currentMethod.getName();
}
String desc = methodMetrics.desc();
if (!StringUtils.hasText(desc)) {
desc = currentMethod.getName();
}
//不需要記錄時間
if (methodMetrics.withoutDuration()) {
Counter counter = Counter.builder(name).tags(methodMetrics.tags()).description(desc).register(meterRegistry);
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new IllegalStateException(e);
} finally {
counter.increment();
}
}
//需要記錄時間(默認(rèn))
Timer timer = Timer.builder(name).tags(methodMetrics.tags()).description(desc).register(meterRegistry);
return timer.record(() -> {
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new IllegalStateException(e);
}
});
}
}代碼很容易,沒什么可說明的,接下來就是在需要記監(jiān)控的地方加上這個注解就行,比如:
@MethodMetrics(name="sms_send",tags = {"vendor","aliyun"})
public void send(String mobile, SendMessage message) throws Exception {
...
}至此,aop形式的指標(biāo)實現(xiàn)方式就完成了。如果是侵入式的話,直接使用meterRegistry就行:
meterRegistry.counter("sms.send","vendor","aliyun").increment();啟動服務(wù),打開http://localhost:8080/actuator/prometheus查看指標(biāo)。
三、高級指標(biāo)之分位數(shù)
分位數(shù)(P50/P90/P95/P99)是我們常用的一個性能指標(biāo),Prometheus提供了兩種解決方案:
client側(cè)計算方案
summery類型,設(shè)置percentiles,在本地計算出Pxx,作為指標(biāo)的一個tag被直接收集。
Timer timer = Timer.builder("sms.send").publishPercentiles(0.5, 0.9, 0.95,0.99).register(meterRegistry);
timer.record(costTime, TimeUnit.MILLISECONDS);會出現(xiàn)四個帶quantile的指標(biāo),如圖:

server側(cè)計算方案
開啟histogram,將所有樣本放入buckets中,在server側(cè)通過histogram_quantile函數(shù)對buckets進行實時計算得出。注意:histogram采用了線性插值法,buckets的劃分對誤差的影響比較大,需合理設(shè)置。
Timer timer = Timer.builder("sms.send")
.publishPercentileHistogram(true)
.serviceLevelObjectives(Duration.ofMillis(10),Duration.ofMillis(20),Duration.ofMillis(50))
.minimumExpectedValue(Duration.ofMillis(1))
.maximumExpectedValue(Duration.ofMillis(100))
.register(meterRegistry);
timer.record(costTime, TimeUnit.MILLISECONDS);會出現(xiàn)一堆xxxx_bucket的指標(biāo),如圖:

然后,使用
histogram_quantile(0.95, rate(sms_send_seconds_bucket[5m]))
就可以看到P95的指標(biāo)了,如圖:

結(jié)論:
方案1適用于單機或只關(guān)心本地運行情況的指標(biāo),比如gc時間、定時任務(wù)執(zhí)行時間、本地緩存更新時間等;
方案2則適用于分布式環(huán)境下的整體運行情況的指標(biāo),比如搜索接口的響應(yīng)時間、第三方接口的響應(yīng)時間等。
相關(guān)文章
java Nio使用NioSocket客戶端與服務(wù)端交互實現(xiàn)方式
這篇文章主要介紹了java Nio使用 NioSocket 客戶端與服務(wù)端交互實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
java實現(xiàn)系統(tǒng)多級文件夾復(fù)制
這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)系統(tǒng)多級文件夾復(fù)制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08
Maven中optional和scope元素的使用弄明白了嗎
這篇文章主要介紹了Maven中optional和scope元素的使用弄明白了嗎,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
日常開發(fā)中,我們很多時候需要用到Java?8的Lambda表達(dá)式,它允許把函數(shù)作為一個方法的參數(shù),讓我們的代碼更優(yōu)雅、更簡潔。所以整理了一波工作中常用的Lambda表達(dá)式??赐暌欢〞袔椭?/div> 2023-03-03
Java在Word中插入上標(biāo)和下標(biāo)的實現(xiàn)方法
在某些情況下,你可能需要在Microsoft?Word中插入上標(biāo)和下標(biāo)。例如,當(dāng)你正在創(chuàng)建一個涉及科學(xué)公式的學(xué)術(shù)文件時,在這篇文章中,你將學(xué)習(xí)如何使用Spire.Doc?for?Java庫在Word文檔中插入上標(biāo)和下標(biāo),需要的朋友可以參考下2022-10-10
SpringBoot中請求入?yún)㈩愋娃D(zhuǎn)換的6種玩法
做 Spring Boot Web 開發(fā)的小伙伴,想必都做過請求入?yún)㈩愋娃D(zhuǎn)換吧,本文將和大家詳細(xì)介紹一下SpringBoot參數(shù)類型轉(zhuǎn)換的6種玩法,希望對大家有所幫助2026-02-02最新評論

