最近需要监控接口的请求数据P90,P99,平均耗时,接口参数包含userId的接口请求数据等等
pom文件
org.projectlombok lombok 1.16.10 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web io.micrometer micrometer-registry-prometheus runtime
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接口查看。
prometheus官网: Download | Prometheus 有windows版本的,直接下载安装即可,prometheus启动时会自动加载同目录下的Prometheus.yml文件,如果需要监听上面的springboot项目,需要在配置文件当中进行添加
scrape_configs: # The job name is added as a label `job=` 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= ` 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: Prometheus-配置解析 - dragonliu - 博客园 这篇文章讲解了prometheus的配置文件。
PS:prometheus默认端口是9090,如需修改需要在启动时添加参数,尝试过在配置文件当中添加,并不成功
PS: windows在双击prometheus.exe后可能出现无法运行的情况,说的是什么windows版本兼容性,防火墙拦截了什么的,网上自行百度下解决方案,我是直接换的电脑。
PS: prometheus有自己的数据查询语法:PromQL 一些复杂的需要自行百度学习
登录可视化界面在搜索当中输入up 然后点击execute可以看到当前监控的job状态 1表示监控当中,0表示监控信息获取失败
grafana官网: Download Grafana | Grafana Labs 。
有windows版本,可以直接下载安装即可,默认端口是3000,默认账号密码是admin
登陆后直接在设置里面的Data Sourecs管理添加Prometheus数据源:输入数据源地址保存即可
点击添加,然后创建面板,然后添加panel
1 输入PromQL查询语句
2 设置展示数据的名称
3 设置数据展示的图表:直方图,折线图,饼图等
4 设置图表名称
当设置好之后点击run query即可出现数据。
grafana是一个功能非常强大的数据可视化软件,使用教程也是需要仔细学习一下。
默认是没有开启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 MeterRegistryCustomizermetricsCommonTags() { 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看到相应的信息
每分钟请求总数
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"})