pom.xml引入相關依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.olive</groupId>
<artifactId>prometheus-meter-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath />
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Prometheus registry -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
直接使用micrometer
核心包的類進行指標定義和註冊
package com.olive.monitor;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
@Component
public class NativeMetricsMontior {
/**
* 支付次數
*/
private Counter payCount;
/**
* 支付金額統計
*/
private DistributionSummary payAmountSum;
@Autowired
private MeterRegistry registry;
@PostConstruct
private void init() {
payCount = registry.counter("pay_request_count", "payCount", "pay-count");
payAmountSum = registry.summary("pay_amount_sum", "payAmountSum", "pay-amount-sum");
}
public Counter getPayCount() {
return payCount;
}
public DistributionSummary getPayAmountSum() {
return payAmountSum;
}
}
通過引入micrometer-registry-prometheus
包,該包結合prometheus,對micrometer進行了封裝
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
同樣定義兩個metrics
package com.olive.monitor;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
@Component
public class PrometheusMetricsMonitor {
/**
* 訂單發起次數
*/
private Counter orderCount;
/**
* 金額統計
*/
private Counter orderAmountSum;
@Autowired
private CollectorRegistry registry;
@PostConstruct
private void init() {
orderCount = Counter.build().name("order_request_count")
.help("order request count.")
.labelNames("orderCount")
.register();
orderAmountSum = Counter.build().name("order_amount_sum")
.help("order amount sum.")
.labelNames("orderAmountSum")
.register();
registry.register(orderCount);
registry.register(orderAmountSum);
}
public Counter getOrderCount() {
return orderCount;
}
public Counter getOrderAmountSum() {
return orderAmountSum;
}
}
prometheus 4種常用Metrics
Counter
連續增加不會減少的計數器,可以用於記錄只增不減的型別,例如:網站存取人數,系統執行時間等。
對於Counter型別的指標,只包含一個inc()的方法,就是用於計數器+1.
一般而言,Counter型別的metric指標在冥冥中我們使用_total結束,如http_requests_total.
Gauge
可增可減的儀表盤,曲線圖
對於這類可增可減的指標,用於反應應用的當前狀態。
例如在監控主機時,主機當前空閒的記憶體大小,可用記憶體大小等等。
對於Gauge指標的物件則包含兩個主要的方法inc()和dec(),用於增加和減少計數。
Histogram
主要用來統計資料的分佈情況,這是一種特殊的metrics資料型別,代表的是一種近似的百分比估算數值,統計所有離散的指標資料在各個取值區段內的次數。例如:我們想統計一段時間內http請求響應小於0.005秒、小於0.01秒、小於0.025秒的資料分佈情況。那麼使用Histogram採集每一次http請求的時間,同時設定bucket。
Summary
Summary和Histogram非常相似,都可以統計事件發生的次數或者大小,以及其分佈情況,他們都提供了對時間的計數_count以及值的彙總_sum,也都提供了可以計算統計樣本分佈情況的功能,不同之處在於Histogram可以通過histogram_quantile函數在伺服器計算分位數。而Sumamry的分位數則是直接在使用者端進行定義的。因此對於分位數的計算,Summary在通過PromQL進行查詢的時候有更好的效能表現,而Histogram則會消耗更多的資源,但是相對於使用者端而言Histogram消耗的資源就更少。用哪個都行,根據實際場景自由調整即可。
定義兩個controller分別使用NativeMetricsMontior
和PrometheusMetricsMonitor
package com.olive.controller;
import java.util.Random;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.olive.monitor.NativeMetricsMontior;
@RestController
public class PayController {
@Resource
private NativeMetricsMontior monitor;
@RequestMapping("/pay")
public String pay(@RequestParam("amount") Double amount) throws Exception {
// 統計支付次數
monitor.getPayCount().increment();
Random random = new Random();
//int amount = random.nextInt(100);
if(amount==null) {
amount = 0.0;
}
// 統計支付總金額
monitor.getPayAmountSum().record(amount);
return "支付成功, 支付金額: " + amount;
}
}
package com.olive.controller;
import java.util.Random;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.olive.monitor.PrometheusMetricsMonitor;
@RestController
public class OrderController {
@Resource
private PrometheusMetricsMonitor monitor;
@RequestMapping("/order")
public String order(@RequestParam("amount") Double amount) throws Exception {
// 訂單總數
monitor.getOrderCount()
.labels("orderCount")
.inc();
Random random = new Random();
//int amount = random.nextInt(100);
if(amount==null) {
amount = 0.0;
}
// 統計訂單總金額
monitor.getOrderAmountSum()
.labels("orderAmountSum")
.inc(amount);
return "下單成功, 訂單金額: " + amount;
}
}
啟動服務
存取http://127.0.0.1:9595/actuator/prometheus
;正常看到監測資料
改變amount多次方式http://127.0.0.1:8080/order?amount=100
和http://127.0.0.1:8080/pay?amount=10
後;再存取http://127.0.0.1:9595/actuator/prometheus
。檢視監控資料
專案中按照上面說的方式進行資料埋點監控不太現實;在spring專案中基本通過AOP進行埋點監測。比如寫一個切面Aspect
;這樣的方式就非常友好。能在入口就做了資料埋點監測,無須在controller裡進行程式碼編寫。
package com.olive.aspect;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import io.micrometer.core.instrument.Metrics;
@Aspect
@Component
public class PrometheusMetricsAspect {
// 切入所有controller包下的請求方法
@Pointcut("execution(* com.olive.controller..*.*(..))")
public void controllerPointcut() {
}
@Around("controllerPointcut()")
public Object MetricsCollector(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userId = StringUtils.hasText(request.getParameter("userId")) ?
request.getParameter("userId") : "no userId";
// 獲取api url
String api = request.getServletPath();
// 獲取請求方法
String method = request.getMethod();
long startTs = System.currentTimeMillis();
LocalDate now = LocalDate.now();
String[] tags = new String[10];
tags[0] = "api";
tags[1] = api;
tags[2] = "method";
tags[3] = method;
tags[4] = "day";
tags[5] = now.toString();
tags[6] = "userId";
tags[7] = userId;
String amount = StringUtils.hasText(request.getParameter("amount")) ?
request.getParameter("amount") : "0.0";
tags[8] = "amount";
tags[9] = amount;
// 請求次數加1
//自定義的指標名稱:custom_http_request_all,指標包含資料
Metrics.counter("custom_http_request_all", tags).increment();
Object object = null;
try {
object = joinPoint.proceed();
} catch (Exception e) {
//請求失敗次數加1
Metrics.counter("custom_http_request_error", tags).increment();
throw e;
} finally {
long endTs = System.currentTimeMillis() - startTs;
//記錄請求響應時間
Metrics.timer("custom_http_request_time", tags).record(endTs, TimeUnit.MILLISECONDS);
}
return object;
}
}
編寫好切面後,重啟服務;存取controller的介面,同樣可以進行自定義監控指標埋點