目录
- 1.创建项目
- 2.自定义指标
- 方式一
- 方式二
- 3. 测试
- 4.项目中的应用
1.创建项目
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> |
2.自定义指标
方式一
直接使用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; | |
public class NativeMetricsMontior { | |
/** | |
* 支付次数 | |
*/ | |
private Counter payCount; | |
/** | |
* 支付金额统计 | |
*/ | |
private DistributionSummary payAmountSum; | |
private MeterRegistry registry; | |
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; | |
public class PrometheusMetricsMonitor { | |
/** | |
* 订单发起次数 | |
*/ | |
private Counter orderCount; | |
/** | |
* 金额统计 | |
*/ | |
private Counter orderAmountSum; | |
private CollectorRegistry registry; | |
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消耗的资源就更少。用哪个都行,根据实际场景自由调整即可。
3. 测试
定义两个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; | |
public class PayController { | |
private NativeMetricsMontior monitor; | |
public String pay( 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; | |
public class OrderController { | |
private PrometheusMetricsMonitor monitor; | |
public String order( 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
。查看监控数据
4.项目中的应用
项目中按照上面说的方式进行数据埋点监控不太现实;在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; | |
public class PrometheusMetricsAspect { | |
// 切入所有controller包下的请求方法 | |
public void 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的接口,同样可以进行自定义监控指标埋点