监控的意义
- 监控服务状态是否宕机
- 监控服务运行指标(内存,虚拟机,线程,请求等)
- 监控日志
- 管理服务(服务下线)
监控的实施方式
比如监控日志的时候并不会对所有的服务都进行监控
而是独立一个服务汇总所有的运行信息,然后监控这个一个服务就行了。
- 方式一 红箭头:主动拉取 (大部分企业中的监控都是主动拉取的方式)
- 方式二 黑箭头:被动等待
所以哪一个应用需要监控,需要把信息给监控的服务。
- 即一个服务如果想要被监控,就把自己的信息主动上报给监控服务中去。
- 并且还要上报自己想要被监控的信息,即:哪种信息想被监控。
可视化监控平台
Spring Boot Admin 监控
源码和对应的版本号具体在GitHub可以查看
不是spring官方开发的,所以spring官方集成,所以版本号还是需要写的。
开源社区项目,用于管理和监控SpringBoot应用程序。
客户端注册到服务端后,通过Http请求方式,服务端定期从客户端获取对应的信息,并通过UI界面展示对应信息。
创建服务端(提供监控的微服务)
第一步:添加 springboot admin 服务端依赖
可以直接在创建模块的时候勾选功能。在Ops中勾选
有客户端有服务端。如果通过这种方式添加的依赖会自动会给你将版本号添加上
- 这种方式系统自动给你添加的依赖如下:
意思就是给你控制了版本号而已,
<properties>
<spring-boot-admin.version>2.6.8</spring-boot-admin.version>
</properties>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<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>
- 看着这么短,其实就是控制了版本号 等同于下边
springboot admin的版本号虽然不是官方开发的,但是版本差不多都是和springboot对应的,你springboot什么版本,这个admin依赖差不多就是什么版本,有的版本如果没有的话就用上一个,不影响
- 是多想被纳入Spring鱼塘
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.6.8</version>
</dependency>
第二步:既然是一个服务,所以该服务需要是一个web服务
还需要配置一个端口,自定义
server:
port: 8080
第三步:启动类上使用注解开启服务
@EnableAdminServer
开启admin server服务
@SpringBootApplication
// 开启服务
@EnableAdminServer
public class SpringbootServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootServerApplication.class, args);
}
}
运行查看界面:
访问8080端口 : http://localhost:8080/
创建客户端(想要被监控的微服务)
第一步:添加依赖
依赖也可以选择,具体导入后的内容见上文服务端中。
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.6.8</version>
</dependency>
第二步:application.yml中配置相关信息
server:
port: 8081
spring:
boot:
admin:
client:
url: http://localhost:8080 # 提供监控服务的地址
启动后查看监控界面
还是访问 http://localhost:8080 发现现在被监控的应用中多了一个了,就是我们刚刚启动的客户端。
- 点击具体的在线应用,可以看详情
- 但是发现我们现在啥都没有监控到,这是因为我们没有配置我想让什么内容被监控。我们还需要设置。
设置哪些信息(端点)需要被监控
management:
endpoint: # 这种一个一个设置
health:
show-details: always # 针对 health 端点,显示健康详情
endpoints: # 这种批量设置
web:
exposure:
# include: health # 默认是health
# include: health,info # 设置多个
include: "*" # * 表示查看全部 注意一定要加引号
exclude: info # 要排除的 排除的优先级高 就算include中设置了也会排除 无论书写先后顺序
endpoint
: 可以精细化控制要被监控的端点,前提需要在**endpoints
**中写了,否则无效!
endpoints
: 表示端点是否被监控,如果不写默认有一个health
如果是* 一般有13个端点: 启动服务时日志信息如下:
注意1:endpoint
和endpoints
****不存在优先级关系
没有优先级的关系,!!!!!!!!!
只有**endpoints
**中开启后 endpoint
才可以进行精细化控制,否则无效。
注意2:Either health or status endpoint must be enabled! 错误
这类错误通常出现在 Spring Boot Actuator 配置中,表示你需要至少启用 health
或 status
端点,才能正常使用 Actuator 的功能
在 Spring Boot Actuator 中,health
和 status
是应用程序状态的重要监控端点。如果它们都被禁用,那么 Actuator 将无法提供基本的健康和状态信息,这会导致上面的错误
同时监控多个客户端(服务)
如果监控多个客户端(微服务)的时候,如果不给微服务起名字,微服务都是没有名字的,此时微服务都会被当做无名的应用的多个实例
# 服务1中
spring:
application:
name: client01
# 服务2中
spring:
application:
name: client02
- 应用数 2
- 实例数 2
发现client01这个应用有两个实例,这两个实例可以分别进行监控,只需要点进去就可以看该实例的详细信息。
监控的原理
- 我们可以通过请求路径来获取数据。
- 一些重要的端点(红色的)
监控端点控制
info端点指标控制
方法一:在application.yml中配置自定义的信息
使用info.自定义名字 来自定义 info端点中的属性。
- 除了info是指定的名字外,其他的key value都是自定义的
info:
appName: "我是客户端02"
company: "阿栩公司"
author: "axuxu"
遇到的问题:高版本springboot的显示问题:
在项目中高版本的springboot中可能定义了属性但是在平台中还是没有显示
在较高版本的 Spring Boot(如 2.5.x 或更新版本)中,Spring Boot Actuator 对 info
端点的配置进行了增强和更加严格的控制,导致某些默认信息不再自动暴露。如果你希望在 /actuator/info
端点中显示环境相关的信息,确实需要通过配置 management.info.env.enabled: true
来手动启用它。
- 需要添加
management:
info:
env:
enabled: true
这个是 Spring Boot Actuator 的一个配置项,它允许将应用程序的环境变量信息暴露到 Actuator 的 /actuator/info
端点中。
但是由于可能敏感信息会暴露,所以通常建议在生产环境中谨慎使用,或者限制此信息的暴露
监控页面的结果
方法二(强烈推荐):通过创建javaBean来控制info的信息
上边的哪一种虽然可以,但是如果是一个复杂的配置,在yml中很难进行配置,比如说,value是一个对象的话,此时在yml也能配置,但是很麻烦
代码实现:
- 此时就可以创建一个Bean加入Spring的管理,通过Spring的自动装配来自动实现info信息的控制。
- 继承
InfoContributor
接口 并实现contribute
方法 然后通过@Component
加入spring容器的管理即可。 - builder.withDetail(String,Object) 来添加一组key value
- builder.withDetails(Map<String,Object>) 来批量添加key value
- 注意:value是Object可以是任何类型,比如:Map集合,实体对象
/**
* 但是使用把此类创建一个bean加入spring管理的时候
* 不能再yml中启动 management.info.env.enabled: true
* 否则会报错,因为如果为true spring会自动创建一个bean 此时就冲突了
*/
@Component
public class EnvInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
// 添加一组键值对 其中value是Object类型 可以为任何数据
builder.withDetail("welcome","hello");
// value为一个map
Map<String, Object> envDetails = new HashMap<>();
envDetails.put("JAVA_HOME", System.getenv("JAVA_HOME"));
envDetails.put("USER_HOME", System.getProperty("user.home"));
// 定义key value 其中value可以是一个map等
builder.withDetail("environment", envDetails);
// value为一个对象
// 是一个对象也可以,且会自定进行转化,不用实现序列化接口
Book book =new Book(1,"数学",12.34);
builder.withDetail("book",book);
// 批量添加键值对,把map集合中的全部添加进来
Map<String,Object> map =new HashMap<>();
map.put("username","axuxu");
map.put("age",18);
map.put("birthday",new Date());
builder.withDetails(map);
}
}
注意事项:
使用把此类创建一个bean加入spring管理的时候,不能再yml中启动 management.info.env.enabled: true 否则会报错,因为如果为true spring会自动创建一个bean 此时就冲突了
即:现在常用的springboot2.5.X及以上版本中
因为application.yml的方式需要设置 management.info.env.enabled: true
所以application.yml的方式和通过javaBean的方式只能存在一种
否则就会报错
- 报错信息如下,不用看,反正没人会闲着到处写,如果真有人这样写,就是闲着没事写着玩的。
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2024-09-09 17:52:27.752 ERROR 39472 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'envInfoContributor', defined in class path resource [org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.class], could not be registered. A bean with that name has already been defined in file [E:\JavaCode\SpringBoot2AndUseJDK8\springboot-client02\target\classes\cn\axuxu\springbootclient02\contributor\EnvInfoContributor.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
已与地址为 ''127.0.0.1:6435',传输: '套接字'' 的目标虚拟机断开连接
进程已结束,退出代码为 1
结果
- 监控页面中的显示
- postman中发送请求获取的数据
http://localhost:8083/actuator/info
{
"welcome": "hello",
"environment": {
"USER_HOME": "C:\\Users\\zhang",
"JAVA_HOME": "D:\\jdk8"
},
"book": {
"id": 1,
"name": "数学",
"price": 12.34
},
"birthday": "2024-09-09T09:39:38.372+00:00",
"age": 18,
"username": "axuxu"
}
health端点指标控制
这个端点指标显示的是你的组件的运行状况。
- 在yml中不能自定义控制
- 但是可以通过java代码的方式来自定义
通过继承 AbstractHealthIndicator 抽象类 或者 实现 HealthIndicator 接口都可以
- 实现 doHealthCheck 方法
@Component
public class HealthContributor extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Map<String,Object> map =new HashMap<>();
map.put("username","axuxu");
map.put("age",18);
map.put("birthday",new Date());
builder.withDetails(map);
}
}
此时运行结果
- 此时发现 名字为空 状态为UNKNOWN 未知
如何设置名字和状态呢?
- @Component(“MyHealth”) 设置名字为 MyHealth
- builder.up();设置状态为 UP
@Component("MyHealth")
public class HealthContributor extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Map<String,Object> map =new HashMap<>();
map.put("username","axuxu");
map.put("age",18);
map.put("birthday",new Date());
builder.withDetails(map);
// 设置状态 一共四种 up down unknown outOfService
builder.up();
}
}
- 配置信息有什么用?
- 可以通过if else 等任何逻辑操作来控制信息和状态等。
if (true){
...操作...
}else {
//这个是控制整个实例的状态
builder.status(Status.OUT_OF_SERVICE);
}
metrics端点指标控制(常用)
- 对应的部位,为监控页面中的性能页面
- 我们可以检测接口中的调用次数
- 还可以自定义一个选项
@Service
public class BookService {
private Counter counter;
// 也可以通过Set注入,但是为了直接创建一个计数器,我们使用构造注入测试方便点
// @Autowired
// MeterRegistry meterRegistry;
public BookService(MeterRegistry meterRegistry){
counter = meterRegistry.counter("用户操作次数:");
}
public double add(){
//在想要计数的地方可以进行计数
counter.increment();
return counter.count();
}
}
@RestController
@RequestMapping("books")
public class BookController {
@Autowired
BookService bookService;
@PostMapping
public double add(){
return bookService.add();
}
}
在性能中有一个用户操作次数的选项
发几次请求http://localhost:8083/books 刷新一下
发现实时监控。这个可以用来计算普通用户操作的免费次数,到次数必须充值vip 啊,恶龙竟是我自己。
自定义端点控制
代码编写
- 在类上添加 @Endpoint 表示这是一个端点bean
- 在方法中添加方法,表示那种请求跳转到那个方法中 , 目前只有三种 没有put请求对应的
- @ReadOperation get请求
- @WriteOperation post请求
- @DeleteOperation delete请求
@Component
/* 如果是自定义端点,必须添加注解声明自己为端点
id为端点的名字,enableByDefault表示是否自动开启,默认为true 所以可以不写
* */
@Endpoint(id = "pay",enableByDefault = true)
public class PayEndpoint {
@ReadOperation
public Object getPay(){
System.out.println("正在支付.......");
// 也可以返回map等任何数据 返回值类型也是自己定义的
return new User(1,"张三","Svip会员","已经充值20000元");
}
@WriteOperation
public Object post(){
return "post请求执行的方法";
}
@DeleteOperation
public Object del(){
return "delete请求执行的方法";
}
}
发送请求及其结果
- 面板“映射”中查看