SpringBoot高级篇-监控
今日目标
掌握SpringBoot监控
在说监控之前,需要回顾一下软件业的发展史。最早的软件完成一些非常简单的功能,代码不多,错误也少。随着软件功能的逐步完善,软件的功能变得越来越复杂,功能不能得到有效的保障,这个阶段出现了针对软件功能的检测,也就是软件测试。伴随着计算机操作系统的逐步升级,软件的运行状态也变得开始让人捉摸不透,出现了不稳定的状况。伴随着计算机网络的发展,程序也从单机状态切换成基于计算机网络的程序,应用于网络的程序开始出现,由于网络的不稳定性,程序的运行状态让使用者更加堪忧。互联网的出现彻底打破了软件的思维模式,随之而来的互联网软件就更加凸显出应对各种各样复杂的网络情况之下的弱小。计算机软件的运行状况已经成为了软件运行的一个大话题,针对软件的运行状况就出现了全新的思维,建立起了初代的软件运行状态监控。
思考: 什么是监控?
就是通过软件的方式展示另一个软件的运行情况,运行的情况则通过各种各样的指标数据反馈给监控人员。例如网络是否顺畅、服务器是否在运行、程序的功能是否能够整百分百运行成功,内存是否够用,等等等等。
监控的意义
监控指标:
监控服务状态是否处理宕机状态
对于现代的互联网程序来说,规模越来越大,功能越来越复杂,还要追求更好的客户体验,因此要监控的信息量也就比较大了。由于现在的互联网程序大部分都是基于微服务的程序,一个程序的运行需要若干个服务来保障,因此第一个要监控的指标就是服务是否正常运行,也就是监控服务状态是否处理宕机状态。
监控服务运行指标
若发现某个服务宕机了,必须马上给出对应的解决方案,避免整体应用功能受影响。其次,由于互联网程序服务的客户量是巨大的,当客户的请求在短时间内集中达到服务器后,就会出现各种程序运行指标的波动。比如内存占用严重,请求无法及时响应处理等,这就是第二个要监控的重要指标,监控服务运行指标。
监控程序运行日志
虽然软件是对外提供用户的访问需求,完成对应功能的,但是后台的运行是否平稳,是否出现了不影响客户使用的功能隐患,这些也是要密切监控的,此时就需要在不停机的情况下,监控系统运行情况,日志是一个不错的手段。如果在众多日志中找到开发者或运维人员所关注的日志信息,简单快速有效的过滤出要看的日志也是监控系统需要考虑的问题,这就是第三个要监控的指标,监控程序运行日志。
管理服务状态
虽然我们期望程序一直平稳运行,但是由于突发情况的出现,例如服务器被攻击、服务器内存溢出等情况造成了服务器宕机,此时当前服务不能满足使用需要,就要将其重启甚至关闭,如果快速控制服务器的启停也是程序运行过程中不可回避的问题,这就是第四个监控项,管理服务状态
通过上述描述,可以看出监控很重要。那具体的监控要如何开展呢?还要从实际的程序运行角度出发。比如现在有3个服务支撑着一个程序的运行,每个服务都有自己的运行状态。

此时被监控的信息就要在三个不同的程序中去查询并展示,但是三个服务是服务于一个程序的运行的,如果不能合并到一个平台上展示,监控工作量巨大,而且信息对称性差,要不停的在三个监控端查看数据。如果将业务放大成30个,300个,3000个呢?看来必须有一个单独的平台,将多个被监控的服务对应的监控指标信息汇总在一起,这样更利于监控工作的开展。
新的程序专门用来监控,新的问题就出现了,是被监控程序主动上报信息还是监控程序主动获取信息?如果监控程序不能主动获取信息,这就意味着监控程序有可能看到的是很久之前被监控程序上报的信息,万一被监控程序宕机了,监控程序就无法区分究竟是好久没法信息了,还是已经下线了。所以监控程序必须具有主动发起请求获取被监控服务信息的能力。
如果监控程序要监控服务时,主动获取对方的信息。那监控程序如何知道哪些程序被自己监控呢?不可能在监控程序中设置我监控谁,这样互联网上的所有程序岂不是都可以被监控到,这样的话信息安全将无法得到保障。合理的做法只能是在被监控程序启动时上报监控程序,告诉监控程序你可以监控我了。看来需要在被监控程序端做主动上报的操作,这就要求被监控程序中配置对应的监控程序是谁。
被监控程序可以提供各种各样的指标数据给监控程序看,但是每一个指标都代表着公司的机密信息,并不是所有的指标都可以给任何人看的,乃至运维人员,所以对被监控指标的是否开放出来给监控系统看,也需要做详细的设定。以上描述的整个过程就是一个监控系统的基本流程。
总结
监控是一个非常重要的工作,是保障程序正常运行的基础手段 监控的过程通过一个监控程序进行,它汇总所有被监控的程序的信息集中统一展示 被监控程序需要主动上报自己被监控,同时要设置哪些指标被监控
思考下面就要开始做监控了,新的问题就来了,监控程序怎么做呢?难道要自己写吗?肯定是不现实的,如何进行监控,接下来开始说。
2. 可视化监控平台
springboot抽取了大部分监控系统的常用指标,提出了监控的总思想。然后就有好心的同志根据监控的总思想,制作了一个通用性很强的监控系统,因为是基于springboot监控的核心思想制作的,所以这个程序被命名为Spring Boot Admin。
Spring Boot Admin,这是一个开源社区项目,用于管理和监控SpringBoot应用程序。这个项目中包含有客户端和服务端两部分,而监控平台指的就是服务端。我们做的程序如果需要被监控,将我们做的程序制作成客户端,然后配置服务端地址后,服务端就可以通过HTTP请求的方式从客户端获取对应的信息,并通过UI界面展示对应信息。
2.1 Admin 服务端开发
【步骤一】:创建day24-springboot-AdminServer
项目

pom.xml依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
【步骤二】:在引导类上添加注解@EnableAdminServer,声明当前应用启动后作为SpringBootAdmin的服务器使用
@EnableAdminServer
@SpringBootApplication
public class Day24SpringbootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(Day24SpringbootAdminServerApplication.class, args);
}
}
【步骤三】:启动服务器浏览器访问
在浏览器输入地址:http://localhost:8080/applications

2.2 Admin 客户端开发
【步骤一】:创建客户端项目day24-springboot-AdminClient

pom.xml中依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
【步骤二】:设置当前客户端将信息上传到哪个服务器上,通过yml文件配置
spring:
boot:
admin:
client:
url: http://localhost:8080
server:
port: 9999
【步骤三】:启动项目 浏览器访问服务器端
做到这里,这个客户端就可以启动了。启动后再次访问服务端程序,界面如下:

可以看到,当前监控了1个程序,点击进去查看详细信息。

由于当前没有设置开放哪些信息给监控服务器,所以目前看不到什么有效的信息。下面需要做两组配置就可以看到信息了。
开放指定信息给服务器看
允许服务器以HTTP请求的方式获取对应的信息
配置如下:
spring:
boot:
admin:
client:
url: http://localhost:8080
server:
port: 9999
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "*"
上述配置对于初学者来说比较容易混淆。简单解释一下,到下一节再做具体的讲解。springbootadmin的客户端默认开放了13组信息给服务器,但是这些信息除了一个之外,其他的信息都不让通过HTTP请求查看。所以你看到的信息基本上就没什么内容了,只能看到一个内容,就是下面的健康信息。

但是即便如此我们看到健康信息中也没什么内容,原因在于健康信息中有一些信息描述了你当前应用使用了什么技术等信息,如果无脑的对外暴露功能会有安全隐患。通过配置就可以开放所有的健康信息明细查看了。
management:
endpoint:
health:
show-details: always
健康明细信息如下:

目前除了健康信息,其他信息都查阅不了。原因在于其他12种信息是默认不提供给服务器通过HTTP请求查阅的,所以需要开启查阅的内容项,使用*表示查阅全部。记得带引号。
endpoints:
web:
exposure:
include: "*"
配置后再刷新服务器页面,就可以看到所有的信息了。

以上界面中展示的信息量就非常大了,包含了13组信息,有性能指标监控,加载的bean列表,加载的系统属性,日志的显示控制等等。
总结
开发监控服务端需要导入坐标,然后在引导类上添加注解@EnableAdminServer,并将其配置成web程序即可 开发被监控的客户端需要导入坐标,然后配置服务端服务器地址,并做开放指标的设定即可 在监控平台中可以查阅到各种各样被监控的指标,前提是客户端开放了被监控的指标
思考
之前说过,服务端要想监控客户端,需要主动的获取到对应信息并展示出来。但是目前我们并没有在客户端开发任何新的功能,但是服务端确可以获取监控信息,谁帮我们做的这些功能呢?
监控原理
通过查阅监控中的映射指标,可以看到当前系统中可以运行的所有请求路径,其中大部分路径以/actuator开头

首先这些请求路径不是开发者自己编写的,其次这个路径代表什么含义呢?既然这个路径可以访问,就可以通过浏览器发送该请求看看究竟可以得到什么信息。

请求,获取的json信息,如下
{
"_links": {
"self": {
"href": "http://localhost:9999/actuator",
"templated": false
},
"beans": {
"href": "http://localhost:9999/actuator/beans",
"templated": false
},
"caches-cache": {
"href": "http://localhost:9999/actuator/caches/{cache}",
"templated": true
},
"caches": {
"href": "http://localhost:9999/actuator/caches",
"templated": false
},
"health": {
"href": "http://localhost:9999/actuator/health",
"templated": false
},
"health-path": {
"href": "http://localhost:9999/actuator/health/{*path}",
"templated": true
},
"info": {
"href": "http://localhost:9999/actuator/info",
"templated": false
},
"conditions": {
"href": "http://localhost:9999/actuator/conditions",
"templated": false
},
"configprops": {
"href": "http://localhost:9999/actuator/configprops",
"templated": false
},
"configprops-prefix": {
"href": "http://localhost:9999/actuator/configprops/{prefix}",
"templated": true
},
"env-toMatch": {
"href": "http://localhost:9999/actuator/env/{toMatch}",
"templated": true
},
"env": {
"href": "http://localhost:9999/actuator/env",
"templated": false
},
"loggers-name": {
"href": "http://localhost:9999/actuator/loggers/{name}",
"templated": true
},
"loggers": {
"href": "http://localhost:9999/actuator/loggers",
"templated": false
},
"heapdump": {
"href": "http://localhost:9999/actuator/heapdump",
"templated": false
},
"threaddump": {
"href": "http://localhost:9999/actuator/threaddump",
"templated": false
},
"metrics-requiredMetricName": {
"href": "http://localhost:9999/actuator/metrics/{requiredMetricName}",
"templated": true
},
"metrics": {
"href": "http://localhost:9999/actuator/metrics",
"templated": false
},
"scheduledtasks": {
"href": "http://localhost:9999/actuator/scheduledtasks",
"templated": false
},
"mappings": {
"href": "http://localhost:9999/actuator/mappings",
"templated": false
}
}
}
其中每一组数据都有一个请求路径,而在这里请求路径中有之前看到过的health,发送此请求又得到了一组信息,http://localhost:9999/actuator/health
{
"status": "UP",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 296783704064,
"free": 117141426176,
"threshold": 10485760,
"exists": true
}
},
"ping": {
"status": "UP"
}
}
}
当前信息与监控面板中的数据存在着对应关系

原来监控中显示的信息实际上是通过发送请求后得到json数据,然后展示出来。按照上述操作,可以发送更多的以/actuator开头的链接地址,获取更多的数据,这些数据汇总到一起组成了监控平台显示的所有数据。
到这里我们得到了一个核心信息,监控平台中显示的信息实际上是通过对被监控的应用发送请求得到的。那这些请求谁开发的呢?打开被监控应用的pom文件,其中导入了springboot admin的对应的client,在这个资源中导入了一个名称叫做actuator的包。被监控的应用之所以可以对外提供上述请求路径,就是因为添加了这个包。

这个actuator是什么呢?就是监控的端点
Actuator,可以称为端点,描述了一组监控信息,SpringBootAdmin提供了多个内置端点,通过访问端点就可以获取对应的监控信息,也可以根据需要自定义端点信息。通过发送请求路劲**/actuator可以访问应用所有端点信息,如果端点中还有明细信息可以发送请求/actuator/端点名称**来获取详细信息。以下列出了所有端点信息说明:
| ID | 描述 | 默认启用 |
|---|---|---|
| auditevents | 暴露当前应用程序的审计事件信息。 | 是 |
| beans | 显示应用程序中所有 Spring bean 的完整列表。 | 是 |
| caches | 暴露可用的缓存。 | 是 |
| conditions | 显示在配置和自动配置类上评估的条件以及它们匹配或不匹配的原因。 | 是 |
| configprops | 显示所有 @ConfigurationProperties 的校对清单。 | 是 |
| env | 暴露 Spring ConfigurableEnvironment 中的属性。 | 是 |
| flyway | 显示已应用的 Flyway 数据库迁移。 | 是 |
| health | 显示应用程序健康信息 | 是 |
| httptrace | 显示 HTTP 追踪信息(默认情况下,最后 100 个 HTTP 请求/响应交换)。 | 是 |
| info | 显示应用程序信息。 | 是 |
| integrationgraph | 显示 Spring Integration 图。 | 是 |
| loggers | 显示和修改应用程序中日志记录器的配置。 | 是 |
| liquibase | 显示已应用的 Liquibase 数据库迁移。 | 是 |
| metrics | 显示当前应用程序的指标度量信息。 | 是 |
| mappings | 显示所有 @RequestMapping 路径的整理清单。 | 是 |
| scheduledtasks | 显示应用程序中的调度任务。 | 是 |
| sessions | 允许从 Spring Session 支持的会话存储中检索和删除用户会话。当使用 Spring Session 的响应式 Web 应用程序支持时不可用。 | 是 |
| shutdown | 正常关闭应用程序。 | 否 |
| threaddump | 执行线程 dump。 | 是 |
| heapdump | 返回一个 hprof 堆 dump 文件。 | 是 |
| jolokia | 通过 HTTP 暴露 JMX bean(当 Jolokia 在 classpath 上时,不适用于 WebFlux)。 | 是 |
| logfile | 返回日志文件的内容(如果已设置 logging.file 或 logging.path 属性)。支持使用 HTTP Range 头来检索部分日志文件的内容。 | 是 |
| prometheus | 以可以由 Prometheus 服务器抓取的格式暴露指标。 | 是 |
上述端点每一项代表被监控的指标,如果对外开放则监控平台可以查询到对应的端点信息,如果未开放则无法查询对应的端点信息。通过配置可以设置端点是否对外开放功能。使用enable属性控制端点是否对外开放。其中health端点为默认端点,不能关闭。
management:
endpoint:
health: # 端点名称
show-details: always
info: # 端点名称
enabled: true # 是否开放
为了方便开发者快速配置端点,springboot admin设置了13个较为常用的端点作为默认开放的端点,如果需要控制默认开放的端点的开放状态,可以通过配置设置,如下:
management:
endpoints:
enabled-by-default: true # 是否开启默认端点,默认值true
上述端点开启后,就可以通过端点对应的路径查看对应的信息了。但是此时还不能通过HTTP请求查询此信息,还需要开启通过HTTP请求查询的端点名称,使用“*”可以简化配置成开放所有端点的WEB端HTTP请求权限。
management:
endpoints:
web:
exposure:
include: "*"
整体上来说,对于端点的配置有两组信息,一组是endpoints开头的,对所有端点进行配置,一组是endpoint开头的,对具体端点进行配置。
management:
endpoint: # 具体端点的配置
health:
show-details: always
info:
enabled: true
endpoints: # 全部端点的配置
web:
exposure:
include: "*"
enabled-by-default: true
总结
被监控客户端通过添加actuator的坐标可以对外提供被访问的端点功能
端点功能的开放与关闭可以通过配置进行控制
web端默认无法获取所有端点信息,通过配置开放端点功能
4.自定义监控指标
》端点描述了被监控的信息,除了系统默认的指标,还可以自行添加显示的指标,下面就通过3种不同的端点的指标自定义方式来学习端点信息的二次开发。
INFO端点
info端点描述了当前应用的基本信息,可以通过两种形式快速配置info端点的信息
配置形式
在yml文件中通过设置info节点的信息就可以快速配置端点信息
management:
endpoint: # 具体端点的配置
health:
show-details: always
info:
enabled: true
endpoints: # 全部端点的配置
web:
exposure:
include: "*"
enabled-by-default: true
info: #配置info信息 2.5以上需要配置,不然下面的info信息不生效
env:
enabled: true
info:
appName: @project.artifactId@
version: @project.version@
company: springboot葵花宝典
author: zbbmeta
配置完毕后,对应信息显示在监控平台上

也可以通过请求端点信息路径获取对应json信息
posmat 请求地址http://localhost:9999/actuator/info
,结果:
{
"appName": "day24-springboot-AdminClient",
"version": "0.0.1-SNAPSHOT",
"company": "springboot葵花宝典",
"author": "zbbmeta"
}
编程形式
通过配置的形式只能添加固定的数据,如果需要动态数据还可以通过配置bean的方式为info端点添加信息,此信息与配置信息共存
package com.zbbmeta.config;
/**
* @author springboot葵花宝典
* @description: TODO
*/
@Component
public class InfoConfig implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("runTime",System.currentTimeMillis()); //添加单个信息
Map infoMap = new HashMap();
infoMap.put("buildTime","2023");
builder.withDetails(infoMap); //添加一组信息
}
}

Health端点
health端点描述当前应用的运行健康指标,即应用的运行是否成功。通过编程的形式可以扩展指标信息。
package com.zbbmeta.config;
/**
* @author springboot葵花宝典
* @description: TODO
*/
@Component
public class HealthConfig extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
boolean condition = true;
if(condition) {
builder.status(Status.UP); //设置运行状态为启动状态
builder.withDetail("runTime", System.currentTimeMillis());
Map infoMap = new HashMap();
infoMap.put("buildTime", "2006");
builder.withDetails(infoMap);
}else{
builder.status(Status.OUT_OF_SERVICE); //设置运行状态为不在服务状态
builder.withDetail("上线了吗?","你做梦");
}
}
}
当任意一个组件状态不为UP时,整体应用对外服务状态为非UP状态。

Metrics端点
metrics端点描述了性能指标,除了系统自带的监控性能指标,还可以自定义性能指标。
package com.zbbmeta.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zbbmeta.entity.R;
import com.zbbmeta.entity.Student;
import com.zbbmeta.mapper.StudentMapper;
import com.zbbmeta.service.IStudentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
/**
* <p>
* 服务实现类
* </p>
*
* @author springboot葵花宝典
* @since 2023-08-20
*/
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {
private Counter counter;
public StudentServiceImpl(MeterRegistry meterRegistry){
counter = meterRegistry.counter("用户付费操作次数:");
}
/**
* 条件+分页查询
* @param currentPage
* @param pageSize
* @param student
* @return
*/
@Override
public IPage<Student> getPage(int currentPage,int pageSize, Student student){
//每次执行删除业务等同于执行了付费业务
counter.increment();
//1 创建IPage分页对象,设置分页参数
IPage<Student> page=new Page<>(currentPage,pageSize);
//2 执行分页查询
LambdaQueryWrapper<Student> lqw = new LambdaQueryWrapper<>();
if(student!=null){
lqw.eq(StrUtil.isNotEmpty(student.getStuid()),Student::getStuid,student.getStuid());
lqw.like(StrUtil.isNotEmpty(student.getName()),Student::getName,student.getName());
lqw.like(StrUtil.isNotEmpty(student.getDept()),Student::getDept,student.getDept());
lqw.like(StrUtil.isNotEmpty(student.getAddress()),Student::getAddress,student.getAddress());
}
IPage<Student> studentList = this.page(page, lqw);
//如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
return studentList;
}
}
在性能指标中就出现了自定义的性能指标监控项

自定义端点
可以根据业务需要自定义端点,方便业务监控
package com.zbbmeta.config;
/**
* @author springboot葵花宝典
* @description: TODO
*/
@Component
@Endpoint(id="pay",enableByDefault = true)
public class PayEndpoint {
@ReadOperation
public Object getPay(){
Map payMap = new HashMap();
payMap.put("level 1","300");
payMap.put("level 2","291");
payMap.put("level 3","666");
return payMap;
}
}
由于此端点数据spirng boot admin无法预知该如何展示,所以通过界面无法看到此数据,通过HTTP请求路径可以获取到当前端点的信息,但是需要先开启当前端点对外功能,或者设置当前端点为默认开发的端点。

总结
端点的指标可以自定义,但是每种不同的指标根据其功能不同,自定义方式不同 info端点通过配置和编程的方式都可以添加端点指标 health端点通过编程的方式添加端点指标,需要注意要为对应指标添加启动状态的逻辑设定 metrics指标通过在业务中添加监控操作设置指标 可以自定义端点添加更多的指标
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!




