1.基础配置
(1)spring boot 版本: 2.3.12.RELEASE
(2)应用名称配置为: seckill-client (配置正式应用名后注意修改以下相关配置)
(3)prometheus:prometheus-2.40.5.windows-amd64
(4)grafana:grafana-9.3.1
2.引入prometheus相关依赖包
<!--prometheus 监控依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--prometheus 依赖-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.8.2</version>
</dependency>
3.配置文件
server:
port: 9001
spring:
application:
# 服务名称
name: seckill-client
#promethues配置
management:
# server:
# #管理端口,不配置则使用程序端口
# port: 9002
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
endpoints:
web:
exposure:
include: health,info,env,prometheus,metrics,httptrace,threaddump,heapdump,springmetrics
# include:
metrics:
export:
prometheus:
enabled: true
tags: ${spring.application.name}
4.增加开启P90,P99统计
package com.rendu.common.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;
/**
* @author mayiengly
* @Date 2022-12-05 18:16
* @Description
*/
@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;
}
}
});
};
}
}
5.切面监控api
package com.rendu.common.aspect;
import io.micrometer.core.instrument.Metrics;
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;
/**
* @author mayiengly
* @Date 2022-12-05 17:23
* @Description
*/
@Aspect
@Component
public class PrometheusMetricsAspect {
// 切入所有controller包下的请求方法
@Pointcut("execution(* com.rendu.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;
}
}
6.查看prometheus数据
浏览器访问:http://127.0.0.1:9001/actuator/prometheus
如下图所示即代表成功
7.配置prometheus
下载prometheus,在prometheus.yml文件的【scrape_configs】中增加监控配置,并运行prometheus.exe启动prometheus
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "seckill-client"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ["127.0.0.1:9001"]
8.测试查询prometheus
(1)浏览器访问prometheus:http://127.0.0.1:9090/
(2)执行【up】,如下图所示即代表成功
(3)访问spring boot开发的任意api,用于测试prometheus数据查询
(4)测试prometheus查询命令
http_request_test_all_total{job="seckill-client"}
如下图所示即代表prometheus查询成功
9.下载并运行grafana
(1)运行bin目录下grafana-server.exe
(2)浏览器访问:http://127.0.0.1:3000/
(3)默认登录账号:admin 密码:admin
10.grafana基础配置
(1)将以下内容存储为seckill-client.json文件
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 3,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"description": "",
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 10,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "9.3.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"expr": "http_request_test_all_total{job=\"seckill-client\"}",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "总访问数",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 12,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "9.3.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"exemplar": false,
"expr": "http_request_test_all_total{job=\"seckill-client\"}",
"format": "table",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A"
}
],
"title": "Panel Title",
"transformations": [
{
"id": "merge",
"options": {}
}
],
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"expr": "avg(http_request_test_time_seconds{job=\"seckill-client\" ,quantile =~ \"0.9|0.99\"}) by (job,quantile)",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "P90 P99指标统计",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"expr": "max(http_request_test_time_seconds_max{job=\"seckill-client\"})",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "最大耗时",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"expr": "60*sum(rate(http_request_test_all_total{job=\"seckill-client\"}[1m]))",
"hide": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "每分钟请求总数",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "y7CI3KKVz"
},
"editorMode": "code",
"expr": "(sum(http_request_test_time_seconds{job=\"seckill-client\"})/sum(http_request_test_all_total{job=\"seckill-client\"}))",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "请求平均耗时",
"type": "timeseries"
}
],
"refresh": false,
"schemaVersion": 37,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-30m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "seckill-client-dashboard",
"uid": "PSpzcYFVk",
"version": 5,
"weekStart": ""
}
(2)选择导入模板,在Dashboards中选择导入
(3)上传seckill-client.json文件,可修改名称和uid如下图,完成后导入即可
11.grafana效果图
12.promethues查询语句
(1)每分钟请求总数
60*sum(rate(http_request_test_all_total{}[1m]))
(2)统计指定api=/demo/name
60*sum(rate(http_request_test_all_total{api="/demo/name"}[1m]))
(3)请求平均耗时
(sum(http_request_test_time_seconds{job="seckill-client"})/sum(http_request_test_all_total{job="seckill-client"}))
(4)P90 P99指标统计
avg(http_request_test_time_seconds{job="seckill-client" ,quantile =~ "0.9|0.99"}) by (job,quantile)
(5)最大耗时
max(http_request_test_time_seconds_max{job="seckill-client"})
(6)访问总数
http_request_test_all_total{job=\"seckill-client\"}