• spring boot 接入prometheus+grafana监控API


    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相关依赖包

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-actuatorartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>io.micrometergroupId>
    7. <artifactId>micrometer-registry-prometheusartifactId>
    8. <version>1.8.2version>
    9. dependency>

    3.配置文件

    1. server:
    2. port: 9001
    3. spring:
    4. application:
    5. # 服务名称
    6. name: seckill-client
    7. #promethues配置
    8. management:
    9. # server:
    10. # #管理端口,不配置则使用程序端口
    11. # port: 9002
    12. endpoint:
    13. metrics:
    14. enabled: true
    15. prometheus:
    16. enabled: true
    17. endpoints:
    18. web:
    19. exposure:
    20. include: health,info,env,prometheus,metrics,httptrace,threaddump,heapdump,springmetrics
    21. # include:
    22. metrics:
    23. export:
    24. prometheus:
    25. enabled: true
    26. tags: ${spring.application.name}

    4.增加开启P90,P99统计

    1. package com.rendu.common.config;
    2. import io.micrometer.core.instrument.Meter;
    3. import io.micrometer.core.instrument.MeterRegistry;
    4. import io.micrometer.core.instrument.config.MeterFilter;
    5. import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
    6. import lombok.extern.slf4j.Slf4j;
    7. import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
    8. import org.springframework.context.annotation.Bean;
    9. import org.springframework.context.annotation.Configuration;
    10. import java.time.Duration;
    11. /**
    12. * @author mayiengly
    13. * @Date 2022-12-05 18:16
    14. * @Description
    15. */
    16. @Configuration
    17. @Slf4j
    18. public class MicrometerConfig {
    19. @Bean
    20. MeterRegistryCustomizer metricsCommonTags() {
    21. return registry -> {
    22. registry.config().meterFilter(
    23. new MeterFilter() {
    24. @Override
    25. public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
    26. //匹配http开头并且是timer类型的监控指标
    27. if (id.getType() == Meter.Type.TIMER & id.getName().matches("^(http){1}.*")) {
    28. return DistributionStatisticConfig.builder()
    29. .percentilesHistogram(true)
    30. .percentiles(0.5, 0.90, 0.95, 0.99)
    31. .serviceLevelObjectives(Duration.ofMillis(50).toNanos(),
    32. Duration.ofMillis(100).toNanos(),
    33. Duration.ofMillis(200).toNanos(),
    34. Duration.ofSeconds(1).toNanos(),
    35. Duration.ofSeconds(5).toNanos())
    36. .minimumExpectedValue(Duration.ofMillis(1).toNanos())
    37. .maximumExpectedValue(Duration.ofSeconds(5).toNanos())
    38. .build()
    39. .merge(config);
    40. } else {
    41. return config;
    42. }
    43. }
    44. });
    45. };
    46. }
    47. }

    5.切面监控api

    1. package com.rendu.common.aspect;
    2. import io.micrometer.core.instrument.Metrics;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.annotation.Around;
    5. import org.aspectj.lang.annotation.Aspect;
    6. import org.aspectj.lang.annotation.Pointcut;
    7. import org.springframework.stereotype.Component;
    8. import org.springframework.util.StringUtils;
    9. import org.springframework.web.context.request.RequestContextHolder;
    10. import org.springframework.web.context.request.ServletRequestAttributes;
    11. import javax.servlet.http.HttpServletRequest;
    12. import java.time.LocalDate;
    13. import java.util.concurrent.TimeUnit;
    14. /**
    15. * @author mayiengly
    16. * @Date 2022-12-05 17:23
    17. * @Description
    18. */
    19. @Aspect
    20. @Component
    21. public class PrometheusMetricsAspect {
    22. // 切入所有controller包下的请求方法
    23. @Pointcut("execution(* com.rendu.controller..*.*(..))")
    24. public void controllerPointcut() {
    25. }
    26. @Around("controllerPointcut()")
    27. public Object MetricsCollector(ProceedingJoinPoint joinPoint) throws Throwable {
    28. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    29. String userId = StringUtils.hasText( request.getParameter("userId"))?request.getParameter("userId"):"no userId";
    30. String appId = StringUtils.hasText( request.getParameter("appId"))?request.getParameter("appId"):"no appId";
    31. // 获取api url
    32. String api = request.getServletPath();
    33. // 获取请求方法
    34. String method = request.getMethod();
    35. long timeMillis = System.currentTimeMillis();
    36. LocalDate now = LocalDate.now();
    37. String[] tags = new String[10];
    38. tags[0]="api";
    39. tags[1] = api;
    40. tags[2]="method";
    41. tags[3]=method;
    42. tags[4]="day";
    43. tags[5]=now.toString();
    44. tags[6]="appId";
    45. tags[7]=appId;
    46. tags[8]="userId";
    47. tags[9]=userId;
    48. // 请求次数加1
    49. //自定义的指标名称:http_request_test_all,指标包含数据
    50. Metrics.counter("http_request_test_all",tags).increment();
    51. Object object;
    52. try {
    53. object = joinPoint.proceed();
    54. } catch (Exception e) {
    55. // 请求失败次数加1
    56. Metrics.counter("http_request_test_error",tags).increment();
    57. throw e;
    58. } finally {
    59. long f = System.currentTimeMillis();
    60. long l = f - timeMillis;
    61. //记录请求响应时间
    62. Metrics.timer("http_request_test_time", tags).record(l, TimeUnit.MILLISECONDS);
    63. }
    64. return object;
    65. }
    66. }

    6.查看prometheus数据

    浏览器访问:http://127.0.0.1:9001/actuator/prometheus

    如下图所示即代表成功

     7.配置prometheus

    下载prometheus,在prometheus.yml文件的【scrape_configs】中增加监控配置,并运行prometheus.exe启动prometheus

    1. scrape_configs:
    2. # The job name is added as a label `job=` to any timeseries scraped from this config.
    3. - job_name: "seckill-client"
    4. # metrics_path defaults to '/metrics'
    5. # scheme defaults to 'http'.
    6. metrics_path: '/actuator/prometheus'
    7. static_configs:
    8. - 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文件

    1. {
    2. "annotations": {
    3. "list": [
    4. {
    5. "builtIn": 1,
    6. "datasource": {
    7. "type": "grafana",
    8. "uid": "-- Grafana --"
    9. },
    10. "enable": true,
    11. "hide": true,
    12. "iconColor": "rgba(0, 211, 255, 1)",
    13. "name": "Annotations & Alerts",
    14. "target": {
    15. "limit": 100,
    16. "matchAny": false,
    17. "tags": [],
    18. "type": "dashboard"
    19. },
    20. "type": "dashboard"
    21. }
    22. ]
    23. },
    24. "editable": true,
    25. "fiscalYearStartMonth": 0,
    26. "graphTooltip": 0,
    27. "id": 3,
    28. "links": [],
    29. "liveNow": false,
    30. "panels": [
    31. {
    32. "datasource": {
    33. "type": "prometheus",
    34. "uid": "y7CI3KKVz"
    35. },
    36. "description": "",
    37. "fieldConfig": {
    38. "defaults": {
    39. "mappings": [],
    40. "thresholds": {
    41. "mode": "absolute",
    42. "steps": [
    43. {
    44. "color": "green",
    45. "value": null
    46. },
    47. {
    48. "color": "red",
    49. "value": 80
    50. }
    51. ]
    52. },
    53. "unit": "short"
    54. },
    55. "overrides": []
    56. },
    57. "gridPos": {
    58. "h": 8,
    59. "w": 12,
    60. "x": 0,
    61. "y": 0
    62. },
    63. "id": 10,
    64. "options": {
    65. "colorMode": "background",
    66. "graphMode": "none",
    67. "justifyMode": "auto",
    68. "orientation": "auto",
    69. "reduceOptions": {
    70. "calcs": [
    71. "lastNotNull"
    72. ],
    73. "fields": "",
    74. "values": false
    75. },
    76. "textMode": "auto"
    77. },
    78. "pluginVersion": "9.3.1",
    79. "targets": [
    80. {
    81. "datasource": {
    82. "type": "prometheus",
    83. "uid": "y7CI3KKVz"
    84. },
    85. "editorMode": "code",
    86. "expr": "http_request_test_all_total{job=\"seckill-client\"}",
    87. "legendFormat": "__auto",
    88. "range": true,
    89. "refId": "A"
    90. }
    91. ],
    92. "title": "总访问数",
    93. "type": "stat"
    94. },
    95. {
    96. "datasource": {
    97. "type": "prometheus",
    98. "uid": "y7CI3KKVz"
    99. },
    100. "fieldConfig": {
    101. "defaults": {
    102. "custom": {
    103. "align": "auto",
    104. "displayMode": "auto",
    105. "inspect": false
    106. },
    107. "mappings": [],
    108. "thresholds": {
    109. "mode": "absolute",
    110. "steps": [
    111. {
    112. "color": "green",
    113. "value": null
    114. },
    115. {
    116. "color": "red",
    117. "value": 80
    118. }
    119. ]
    120. }
    121. },
    122. "overrides": []
    123. },
    124. "gridPos": {
    125. "h": 8,
    126. "w": 12,
    127. "x": 12,
    128. "y": 0
    129. },
    130. "id": 12,
    131. "options": {
    132. "footer": {
    133. "fields": "",
    134. "reducer": [
    135. "sum"
    136. ],
    137. "show": false
    138. },
    139. "showHeader": true
    140. },
    141. "pluginVersion": "9.3.1",
    142. "targets": [
    143. {
    144. "datasource": {
    145. "type": "prometheus",
    146. "uid": "y7CI3KKVz"
    147. },
    148. "editorMode": "code",
    149. "exemplar": false,
    150. "expr": "http_request_test_all_total{job=\"seckill-client\"}",
    151. "format": "table",
    152. "instant": true,
    153. "legendFormat": "__auto",
    154. "range": false,
    155. "refId": "A"
    156. }
    157. ],
    158. "title": "Panel Title",
    159. "transformations": [
    160. {
    161. "id": "merge",
    162. "options": {}
    163. }
    164. ],
    165. "type": "table"
    166. },
    167. {
    168. "datasource": {
    169. "type": "prometheus",
    170. "uid": "y7CI3KKVz"
    171. },
    172. "fieldConfig": {
    173. "defaults": {
    174. "color": {
    175. "mode": "palette-classic"
    176. },
    177. "custom": {
    178. "axisCenteredZero": false,
    179. "axisColorMode": "text",
    180. "axisLabel": "",
    181. "axisPlacement": "auto",
    182. "barAlignment": 0,
    183. "drawStyle": "line",
    184. "fillOpacity": 0,
    185. "gradientMode": "none",
    186. "hideFrom": {
    187. "legend": false,
    188. "tooltip": false,
    189. "viz": false
    190. },
    191. "lineInterpolation": "linear",
    192. "lineWidth": 1,
    193. "pointSize": 5,
    194. "scaleDistribution": {
    195. "type": "linear"
    196. },
    197. "showPoints": "auto",
    198. "spanNulls": false,
    199. "stacking": {
    200. "group": "A",
    201. "mode": "none"
    202. },
    203. "thresholdsStyle": {
    204. "mode": "off"
    205. }
    206. },
    207. "mappings": [],
    208. "thresholds": {
    209. "mode": "absolute",
    210. "steps": [
    211. {
    212. "color": "green",
    213. "value": null
    214. },
    215. {
    216. "color": "red",
    217. "value": 80
    218. }
    219. ]
    220. }
    221. },
    222. "overrides": []
    223. },
    224. "gridPos": {
    225. "h": 8,
    226. "w": 12,
    227. "x": 0,
    228. "y": 8
    229. },
    230. "id": 6,
    231. "options": {
    232. "legend": {
    233. "calcs": [],
    234. "displayMode": "list",
    235. "placement": "bottom",
    236. "showLegend": true
    237. },
    238. "tooltip": {
    239. "mode": "single",
    240. "sort": "none"
    241. }
    242. },
    243. "targets": [
    244. {
    245. "datasource": {
    246. "type": "prometheus",
    247. "uid": "y7CI3KKVz"
    248. },
    249. "editorMode": "code",
    250. "expr": "avg(http_request_test_time_seconds{job=\"seckill-client\" ,quantile =~ \"0.9|0.99\"}) by (job,quantile)",
    251. "legendFormat": "__auto",
    252. "range": true,
    253. "refId": "A"
    254. }
    255. ],
    256. "title": "P90 P99指标统计",
    257. "type": "timeseries"
    258. },
    259. {
    260. "datasource": {
    261. "type": "prometheus",
    262. "uid": "y7CI3KKVz"
    263. },
    264. "fieldConfig": {
    265. "defaults": {
    266. "color": {
    267. "mode": "palette-classic"
    268. },
    269. "custom": {
    270. "axisCenteredZero": false,
    271. "axisColorMode": "text",
    272. "axisLabel": "",
    273. "axisPlacement": "auto",
    274. "barAlignment": 0,
    275. "drawStyle": "line",
    276. "fillOpacity": 0,
    277. "gradientMode": "none",
    278. "hideFrom": {
    279. "legend": false,
    280. "tooltip": false,
    281. "viz": false
    282. },
    283. "lineInterpolation": "linear",
    284. "lineWidth": 1,
    285. "pointSize": 5,
    286. "scaleDistribution": {
    287. "type": "linear"
    288. },
    289. "showPoints": "auto",
    290. "spanNulls": false,
    291. "stacking": {
    292. "group": "A",
    293. "mode": "none"
    294. },
    295. "thresholdsStyle": {
    296. "mode": "off"
    297. }
    298. },
    299. "mappings": [],
    300. "thresholds": {
    301. "mode": "absolute",
    302. "steps": [
    303. {
    304. "color": "green",
    305. "value": null
    306. },
    307. {
    308. "color": "red",
    309. "value": 80
    310. }
    311. ]
    312. }
    313. },
    314. "overrides": []
    315. },
    316. "gridPos": {
    317. "h": 8,
    318. "w": 12,
    319. "x": 12,
    320. "y": 8
    321. },
    322. "id": 8,
    323. "options": {
    324. "legend": {
    325. "calcs": [],
    326. "displayMode": "list",
    327. "placement": "bottom",
    328. "showLegend": true
    329. },
    330. "tooltip": {
    331. "mode": "single",
    332. "sort": "none"
    333. }
    334. },
    335. "targets": [
    336. {
    337. "datasource": {
    338. "type": "prometheus",
    339. "uid": "y7CI3KKVz"
    340. },
    341. "editorMode": "code",
    342. "expr": "max(http_request_test_time_seconds_max{job=\"seckill-client\"})",
    343. "legendFormat": "__auto",
    344. "range": true,
    345. "refId": "A"
    346. }
    347. ],
    348. "title": "最大耗时",
    349. "type": "timeseries"
    350. },
    351. {
    352. "datasource": {
    353. "type": "prometheus",
    354. "uid": "y7CI3KKVz"
    355. },
    356. "fieldConfig": {
    357. "defaults": {
    358. "color": {
    359. "mode": "palette-classic"
    360. },
    361. "custom": {
    362. "axisCenteredZero": false,
    363. "axisColorMode": "text",
    364. "axisLabel": "",
    365. "axisPlacement": "auto",
    366. "barAlignment": 0,
    367. "drawStyle": "line",
    368. "fillOpacity": 0,
    369. "gradientMode": "none",
    370. "hideFrom": {
    371. "legend": false,
    372. "tooltip": false,
    373. "viz": false
    374. },
    375. "lineInterpolation": "linear",
    376. "lineWidth": 1,
    377. "pointSize": 5,
    378. "scaleDistribution": {
    379. "type": "linear"
    380. },
    381. "showPoints": "auto",
    382. "spanNulls": false,
    383. "stacking": {
    384. "group": "A",
    385. "mode": "none"
    386. },
    387. "thresholdsStyle": {
    388. "mode": "off"
    389. }
    390. },
    391. "mappings": [],
    392. "thresholds": {
    393. "mode": "absolute",
    394. "steps": [
    395. {
    396. "color": "green",
    397. "value": null
    398. },
    399. {
    400. "color": "red",
    401. "value": 80
    402. }
    403. ]
    404. }
    405. },
    406. "overrides": []
    407. },
    408. "gridPos": {
    409. "h": 8,
    410. "w": 12,
    411. "x": 0,
    412. "y": 16
    413. },
    414. "id": 2,
    415. "options": {
    416. "legend": {
    417. "calcs": [],
    418. "displayMode": "list",
    419. "placement": "bottom",
    420. "showLegend": true
    421. },
    422. "tooltip": {
    423. "mode": "single",
    424. "sort": "none"
    425. }
    426. },
    427. "targets": [
    428. {
    429. "datasource": {
    430. "type": "prometheus",
    431. "uid": "y7CI3KKVz"
    432. },
    433. "editorMode": "code",
    434. "expr": "60*sum(rate(http_request_test_all_total{job=\"seckill-client\"}[1m]))",
    435. "hide": false,
    436. "legendFormat": "__auto",
    437. "range": true,
    438. "refId": "A"
    439. }
    440. ],
    441. "title": "每分钟请求总数",
    442. "type": "timeseries"
    443. },
    444. {
    445. "datasource": {
    446. "type": "prometheus",
    447. "uid": "y7CI3KKVz"
    448. },
    449. "fieldConfig": {
    450. "defaults": {
    451. "color": {
    452. "mode": "palette-classic"
    453. },
    454. "custom": {
    455. "axisCenteredZero": false,
    456. "axisColorMode": "text",
    457. "axisLabel": "",
    458. "axisPlacement": "auto",
    459. "barAlignment": 0,
    460. "drawStyle": "line",
    461. "fillOpacity": 0,
    462. "gradientMode": "none",
    463. "hideFrom": {
    464. "legend": false,
    465. "tooltip": false,
    466. "viz": false
    467. },
    468. "lineInterpolation": "linear",
    469. "lineWidth": 1,
    470. "pointSize": 5,
    471. "scaleDistribution": {
    472. "type": "linear"
    473. },
    474. "showPoints": "auto",
    475. "spanNulls": false,
    476. "stacking": {
    477. "group": "A",
    478. "mode": "none"
    479. },
    480. "thresholdsStyle": {
    481. "mode": "off"
    482. }
    483. },
    484. "mappings": [],
    485. "thresholds": {
    486. "mode": "absolute",
    487. "steps": [
    488. {
    489. "color": "green",
    490. "value": null
    491. },
    492. {
    493. "color": "red",
    494. "value": 80
    495. }
    496. ]
    497. }
    498. },
    499. "overrides": []
    500. },
    501. "gridPos": {
    502. "h": 8,
    503. "w": 12,
    504. "x": 12,
    505. "y": 16
    506. },
    507. "id": 4,
    508. "options": {
    509. "legend": {
    510. "calcs": [],
    511. "displayMode": "list",
    512. "placement": "bottom",
    513. "showLegend": true
    514. },
    515. "tooltip": {
    516. "mode": "single",
    517. "sort": "none"
    518. }
    519. },
    520. "targets": [
    521. {
    522. "datasource": {
    523. "type": "prometheus",
    524. "uid": "y7CI3KKVz"
    525. },
    526. "editorMode": "code",
    527. "expr": "(sum(http_request_test_time_seconds{job=\"seckill-client\"})/sum(http_request_test_all_total{job=\"seckill-client\"}))",
    528. "legendFormat": "__auto",
    529. "range": true,
    530. "refId": "A"
    531. }
    532. ],
    533. "title": "请求平均耗时",
    534. "type": "timeseries"
    535. }
    536. ],
    537. "refresh": false,
    538. "schemaVersion": 37,
    539. "style": "dark",
    540. "tags": [],
    541. "templating": {
    542. "list": []
    543. },
    544. "time": {
    545. "from": "now-30m",
    546. "to": "now"
    547. },
    548. "timepicker": {},
    549. "timezone": "",
    550. "title": "seckill-client-dashboard",
    551. "uid": "PSpzcYFVk",
    552. "version": 5,
    553. "weekStart": ""
    554. }

    (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\"}

  • 相关阅读:
    每日一题 712两个字符串的最小ASCLL删除和
    腾讯云智实习1,2,3面
    GZ033 大数据应用开发赛题第05套
    【云原生】初识 Kubernetes — pod 的前世今生
    Calculus of Variations:变分计算
    基本数据类型的包装类
    React项目中如何实现一个简单的锚点目录定位
    仿闪照功能娱乐微信小程序源码下载-带外卖CPS功能和流量主
    弹性数据库连接池探活策略调研(二)——Druid
    3.5-构建自己的Docker镜像
  • 原文地址:https://blog.csdn.net/RenDuData/article/details/128197186