Springboot应用整合Prometheus+Grafana进行自定义指标监控
最近需要监控接口的请求数据P90,P99,平均耗时,接口参数包含userId的接口请求数据等等
第一步 创建一个springboot项目
pom文件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!--aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--prometheus -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
springboot的actuator可以很好的配合prometheus实现项目的监控,所以这里使用actuator来进行配合
配置信息
spring.application.name="prometheus-test" //建议写上
management.endpoint.metrics.enabled=true //支持metrics
management.endpoints.web.exposure.include=* //开放端口
management.endpoint.prometheus.enabled=true //支持prometheus
management.metrics.export.prometheus.enabled=true
切面实现prometheus自定义监控指标
监控请求数量 ,请求响应时间,请求特定参数
package com.example.prometheus.demo.aspect;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
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 javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class PrometheusMetricsAspect {
// 切入所有controller包下的请求方法
@Pointcut("execution(* com.example.prometheus.demo.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";
String appId = StringUtils.hasText( request.getParameter("appId"))?request.getParameter("appId"):"no appId";
// 获取api url
String api = request.getServletPath();
// 获取请求方法
String method = request.getMethod();
long timeMillis = 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]="appId";
tags[7]=appId;
tags[8]="userId";
tags[9]=userId;
// 请求次数加1
//自定义的指标名称:http_request_test_all,指标包含数据
Metrics.counter("http_request_test_all",tags).increment();
Object object;
try {
object = joinPoint.proceed();
} catch (Exception e) {
// 请求失败次数加1
Metrics.counter("http_request_test_error",tags).increment();
throw e;
} finally {
long f = System.currentTimeMillis();
long l = f - timeMillis;
//记录请求响应时间
Metrics.timer("http_request_test_time", tags).record(l, TimeUnit.MILLISECONDS);
}
return object;
}
}
创建测试用的接口
package com.example.prometheus.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("/user")
public String getInfo(@RequestParam String userId ){
log.info("userId:{}",userId);
return "123";
}
@GetMapping("/app")
public String getAppInfo(@RequestParam String appId ){
log.info("appId:{}",appId);
return "123456";
}
@GetMapping("/user/app")
public String getUserAppInfo(@RequestParam String appId ,@RequestParam String userId ){
log.info("appId:{}",appId);
log.info("userId:{}",userId);
return "abc";
}
}
启动项目 进行测试查看监控数据是否生效
启动项目 首先访问 ip+port/actuator/prometheus 出现以下数据则表示prometheus正确启动当中
访问一下测试接口:127.0.0.1:8080/test/user?userId=2143,然后刷新/actuator/prometheus接口看看数据
可以看到我们自定义的指标:http_request_test_all 和指标里面我们定义的数据信息,同时需要注意的是我们在统计请求次数的时候,这里会自动将我们的指标加上后缀:_total 所以我们后续使用grafana查询的时候也要加上这个后缀。
PS: 可能版本不一样,我在看资料上发现有些后缀是:_count,所以具体是什么需要通过/actuator/prometheus接口查看。
Windows下安装Prometheus
prometheus官网: https://prometheus.io/download/ 有windows版本的,直接下载安装即可,prometheus启动时会自动加载同目录下的Prometheus.yml文件,如果需要监听上面的springboot项目,需要在配置文件当中进行添加
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus-test"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
metrics_path: /actuator/prometheus
static_configs:
- targets: ["localhost:8080"]
其中 prometheus 是默认都有的,下面的prometheus-test是我监听的springboot项目,然后需要配置metrics_path:/actuator/prometheus 这样才能正确获取到springboot应用的信息。
PS: https://www.cnblogs.com/liujiliang/p/10080849.html 这篇文章讲解了prometheus的配置文件。
PS:prometheus默认端口是9090,如需修改需要在启动时添加参数,尝试过在配置文件当中添加,并不成功
PS: windows在双击prometheus.exe后可能出现无法运行的情况,说的是什么windows版本兼容性,防火墙拦截了什么的,网上自行百度下解决方案,我是直接换的电脑。
PS: prometheus有自己的数据查询语法:PromQL 一些复杂的需要自行百度学习
登录可视化界面在搜索当中输入up 然后点击execute可以看到当前监控的job状态 1表示监控当中,0表示监控信息获取失败
Windows下安装Grafana
grafana官网:https://grafana.com/grafana/download?pg=get&plcmt=selfmanaged-box1-cta1&platform=windows 。
有windows版本,可以直接下载安装即可,默认端口是3000,默认账号密码是admin
登陆后直接在设置里面的Data Sourecs管理添加Prometheus数据源:输入数据源地址保存即可
使用grafana展示数据
点击添加,然后创建面板,然后添加panel
1 输入PromQL查询语句
2 设置展示数据的名称
3 设置数据展示的图表:直方图,折线图,饼图等
4 设置图表名称
当设置好之后点击run query即可出现数据。
grafana是一个功能非常强大的数据可视化软件,使用教程也是需要仔细学习一下。
Springboot应用开启P90,P99统计
默认是没有开启P90和P99统计的需要在代码当中进行配置
package com.example.prometheus.demo.config;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
@Slf4j
public class MicrometerConfig {
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> {
registry.config().meterFilter(
new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
//匹配http开头并且是timer类型的监控指标
if (id.getType() == Meter.Type.TIMER & id.getName().matches("^(http){1}.*")) {
return DistributionStatisticConfig.builder()
.percentilesHistogram(true)
.percentiles(0.5, 0.90, 0.95, 0.99)
.serviceLevelObjectives(Duration.ofMillis(50).toNanos(),
Duration.ofMillis(100).toNanos(),
Duration.ofMillis(200).toNanos(),
Duration.ofSeconds(1).toNanos(),
Duration.ofSeconds(5).toNanos())
.minimumExpectedValue(Duration.ofMillis(1).toNanos())
.maximumExpectedValue(Duration.ofSeconds(5).toNanos())
.build()
.merge(config);
} else {
return config;
}
}
});
};
}
}
然后记录请求响应时间
//记录请求响应时间
Metrics.timer("http_request_test_time", tags).record(l, TimeUnit.MILLISECONDS);
发起请求后会在/actuator/prometheus看到相应的信息
统计PromQL
每分钟请求总数
60*sum(rate(http_request_test_all_total{}[1m]))
统计自定义标签api=/test/user
60*sum(rate(http_request_test_all_total{api="/test/user"}[1m]))
请求平均耗时
(sum(http_request_test_time_seconds)/sum(http_request_test_all_total))
P90 P99指标统计
avg(http_request_test_time_seconds{job="prometheus-test" ,quantile =~ "0.9|0.99"}) by (job,quantile)
最大耗时
max(http_request_test_time_seconds_max{job="prometheus-test"})