前言
在之前的 文章 中,我们有提到过如何基于Prometheus+Grafana的搭建去实现Redis,MySQL这类的监控。那么本文我们将会讲解如何实现基于SpringBoot这类Java服务的监控。
SpringBoot端配置
首先你需要下载好Grafana和Prometheus,并且安装相关环境。具体的安装步骤在本文就不做重复提及了。
接着我们需要构建一套SpringBoot的服务,相关依赖内容如下所示:
xml
<properties>
<springboot.web.version>2.4.9</springboot.web.version>
<prometheus.version>1.10.9</prometheus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${springboot.web.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.web.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>${prometheus.version}</version>
</dependency>
</dependencies>
接着我们可以写一个简单的SpringBoot接口服务:
java
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/success")
public String success() {
System.out.println("success");
return "success";
}
@GetMapping("/echo1")
public String echo1() {
return "echo";
}
@GetMapping("/echo2")
public String echo2() {
return "echo";
}
@GetMapping("/echo3")
public String echo3() {
int k = 1/0;
return "echo";
}
@GetMapping("/echo4")
public String echo() {
return "echo";
}
}
到这里,我们所做的东西都是非常简单的步骤,然后就需要我们配置好application.yaml配置文件,并且启动SpringBoot应用:
yml
spring:
jmx:
enabled: true
application:
name: qiyu-live-monitor-springboot-web
management:
endpoints:
web:
exposure:
include:
- health
- prometheus
metrics:
tags:
application: ${spring.application.name}
注意,这里我只开放了health检测和prometheus的端口,用于给监控平台读取,其他端口尽量不要选择开放,注意接口安全性。
启动了之后,可以尝试请求:http://localhost:8080/actuator/ 接口,你会看到当前SpringBoot所有对外暴露的actuator类型接口地址。

接着我们点开如下地址:http://localhost:8080/actuator/prometheus。
你会看到有一串之前没有遇见过的数据内容:

这些数据其实就是我们今天要讲的指标数据,具体如何使用,我们后边会提及。
好了,到这里,SpringBoot服务的配置基本就可以结束了,下边来看Prometheus和Grafana这边的配置。
Prometheus和Grafana配置
首先,我们启动Prometheus和Grafana的时候,大多数情况都是需要依赖一份yaml的规则文件的,而基于它们的监控特点,也是要修改这份文件的。
这里需要注意的点是,Prometheus主要负责通过Http接口去拉取我们业务服务的指标数据。也就是定期拉取上边我们SpringBoot依赖中的spring-boot-starter-actuator的接口。
也就是http://localhost:8080/actuator/prometheus接口中所返回的内容。
所以梳理下来,你会发现Prometheus的执行原理,是采用了拉模型的思路去实现的,各个业务服务只需要暴露一个地址,返回内部服务的指标信息(例如api执行信息,jvm信息等),然后Prometheus这边会定时去拉最新的数据,并且存储在自己的时许数据库中。如下图所示:

这一点的设计,和传统的其他监控平台设计思路不太一样,例如SkyWalking,Pinpoint这类监控平台,都是得引入一些agent包去做上报的动作,所以需要将监控拆解为client端和server端。
而Prometheus这套更多是希望业务服务不需要关心这部分的动作,只需开放接口等着它去采集即可。
好了,现在我们继续进入到实战当中,这里启动Promethus的时候,需要新增配置项:

perl
global: scrape_interval: 15s
scrape_configs: - job_name: "prometheus" static_configs: - targets: ["localhost:9090"]
- job_name: "node_exporter" static_configs: - targets: ["localhost:9100"] labels: instance: node_exporter //这里的job_name可以自定义配置 - job_name: 'qiyu-live-monitor-springboot-web' scrape_interval: 5s metrics_path: '/actuator/prometheus' static_configs: //这里我们就简单配置一个静态ip,如果是集群环境,建议配置注册中心地址 - targets: ['127.0.0.1:8080']
接着启动prometheus:
ini
prometheus --config.file=./prometheus.yml
启动之后,我们可以尝试访问以下地址:
bash
http://localhost:3000/ Grafana地址http://localhost:9090/targets prometheus地址http://localhost:9100/metrics 采集点地址
如果一切正常的话,你应该可以看到以下几个页面:
http://localhost:3000/: 这个是Grafana的登录页面,默认是admin,admin账号。
http://localhost:9100/metrics: 这个地址你可以理解为是各个业务方的指标汇总展示地址,不过数据很杂,展示不友好。

http://localhost:9090/targets: 这个地址可以理解为是Promethus将采集回来的数据统统整理好了,方便我们使用可视化界面来进行管理。这里你仔细观察,还能看到SpringBoot服务采集回来的数据集合。

好了,到这里基本的数据已经整理好了,就剩下画图了。
Grafana绘图步骤
现在,我们有了指标数据后,可以在Grafana上去给它进行绘制了,先来给大家看看我的绘制效果:

这里我只是简单的实现了如何将指标汇集的效果,其实都是使用PromSQL去实现的,下边将SQL内容展示给大家看:
请求总数统计

累计请求总数,这块比较简单,用了一个sum函数,将默认的指标做了下累加:
java
sum(http_server_requests_seconds_count{application=~"$application"})
异常总数统计

这块也是简单的做了个sum计算,只不过对于异常的判断,因为用的是默认的指标,不好做标准化,所以就用了exception字段去进行字符串匹配方式进行判断:
bash
sum(http_server_requests_seconds_count{application=~"$application",exception!~"None"})
每秒HTTP接口并发统计

每秒接口并发量统计,其实是用了increase函数,它的底层就是求某个时间段内的增量数据,底层用的是一个可以重置的计数器去实现的。具体函数如下所示:
java
increase(http_server_requests_seconds_count{application=~"$application"}[60s])
这里可以注意到,我在图中有圈起来一个叫做Legend的东西,这个东西比较容易忽略,它是用于标识统计图中每条折线的标签所用的,这里可以选择用自定义变量去渲染,不过自定义变量要使用双重花括号去表示。
每秒HTTP异常接口并发统计

这块其实和每秒并发统计时类似的,所以我就不做过多解释了,直接贴PromSQL:
java
increase(http_server_requests_seconds_count{application=~"$application",exception!~"None"}[60s])
好了到这里基本上基于Prometheus的指标采集和绘图渲染功能就告一段落了,希望看到这里,你对整个步骤的实践有一定的了解。
自定义指标采集
其实前边的内容你会发现,大部分都是默认的指标采集,也就是说actuator接口默认返回了什么,Prometheus就只能采集什么。而且说实话,默认actuator返回的这些指标,实在是不够用,例如环境类型(uat,pre,grey,prod)缺失,响应状态码缺失等等,所以大多数时候,大家更愿意使用自定义指标采集的方案。
因此官方的jar包中也可以提供了相关的指标API去实现这类自定义的需求,下边时一份我的实践代码,可供大家学习参考:
基于SpringBoot的拦截器去统计各个uri接口的请求次数:
指标采集组件封装:
java
package com.qiyu.monitor.web.config;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @Author idea
* @Date: Created in 09:48 2023/11/12
* @Description
*/
@Component
public class PrometheusComponent implements ApplicationContextAware {
/**
* 请求总数
*/
private Counter requestCounter;
/**
* 正在处理中的请求数
*/
private Gauge duringRequestGauge;
/**
* 请求直方图
*/
private Histogram requestHistogram;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CollectorRegistry collectorRegistry = applicationContext.getBean(CollectorRegistry.class);
requestCounter = Counter.build().name("demo_req_total").labelNames("uri", "method", "code")
.help("请求总数").register(collectorRegistry);
}
public Counter counter() {
return requestCounter;
}
}
在拦截器中注入采集点:
java
package com.qiyu.monitor.web.config;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author idea
* @Date: Created in 09:53 2023/11/12
* @Description
*/
public class PrometheusInterceptor extends HandlerInterceptorAdapter {
private PrometheusComponent prometheusComponent;
public PrometheusInterceptor(PrometheusComponent prometheusComponent) {
this.prometheusComponent = prometheusComponent;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return super.preHandle(request, response, handler);
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String uri = request.getRequestURI();
String method = request.getMethod();
int status = response.getStatus();
prometheusComponent.counter().labels(uri,method, String.valueOf(status)).inc();
super.afterCompletion(request, response, handler, ex);
}
}
最后要记得配置改拦截器:
java
package com.qiyu.monitor.web.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author idea
* @Date: Created in 09:54 2023/11/12
* @Description
*/
@Configuration
@ConditionalOnBean(PrometheusComponent.class)
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
protected PrometheusComponent prometheusComponent;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PrometheusInterceptor(prometheusComponent)).addPathPatterns("/**");
}
}
看到这里,你会发现,其实本质都非常简单,就是构建的api需要多谷歌搜索,熟悉下。
其实类似的功能,我们还可以扩展开来,例如:Kafka消费和发送的指标采集,MyBatis的指标采集,Redis的指标采集,Dubbo的指标采集,SpringCloud Feign的指标采集等等,还有非常多的中间件指标采集待大家去尝试。
希望本文能对你有所启发。