您的当前位置:首页正文

Spring Boot Admin监控的使用

2024-11-28 来源:个人技术集锦

监控的意义

监控的实施方式

比如监控日志的时候并不会对所有的服务都进行监控

而是独立一个服务汇总所有的运行信息,然后监控这个一个服务就行了。

  1. 方式一 红箭头:主动拉取 (大部分企业中的监控都是主动拉取的方式)
  2. 方式二 黑箭头:被动等待

可视化监控平台

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:endpointendpoints****不存在优先级关系

没有优先级的关系,!!!!!!!!!

只有**endpoints**中开启后 endpoint 才可以进行精细化控制,否则无效。

注意2:Either health or status endpoint must be enabled! 错误

这类错误通常出现在 Spring Boot Actuator 配置中,表示你需要至少启用 healthstatus 端点,才能正常使用 Actuator 的功能

在 Spring Boot Actuator 中,healthstatus 是应用程序状态的重要监控端点。如果它们都被禁用,那么 Actuator 将无法提供基本的健康和状态信息,这会导致上面的错误

同时监控多个客户端(服务)

如果监控多个客户端(微服务)的时候,如果不给微服务起名字,微服务都是没有名字的,此时微服务都会被当做无名的应用的多个实例

  1. 启动两个不同的服务,但是不指定服务名的情况下
    • 应用数为 1
    • 实例数为 2

  1. 给不同的服务起名字的情况下
# 服务1中
spring:
  application:
    name: client01
# 服务2中
spring:
  application:
    name: client02
  • 应用数 2
  • 实例数 2

  1. 给同一个服务(同一个服务名字)是一个集群部署的时候

发现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信息的控制。
  1. 继承InfoContributor 接口 并实现 contribute方法 然后通过@Component加入spring容器的管理即可。
  2. builder.withDetail(String,Object) 来添加一组key value
  3. builder.withDetails(Map<String,Object>) 来批量添加key value
  4. 注意: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 啊,恶龙竟是我自己。

自定义端点控制

代码编写

  1. 在类上添加 @Endpoint 表示这是一个端点bean
  2. 在方法中添加方法,表示那种请求跳转到那个方法中 , 目前只有三种 没有put请求对应的
    1. @ReadOperation get请求
    2. @WriteOperation post请求
    3. @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请求执行的方法";
    }
}

发送请求及其结果

  • 面板“映射”中查看

显示全文