动手定制自己的 Health Indicator

Spring Boot 自带的 Health Indicator

目的

  • 检查应用程序的运行状态

状态

  • DOWN - 503
    系统有错误
  • OUT_OF_SERVICE - 503
    系统有错误
  • UP - 200
    系统正常运行
  • UNKNOWN - 200
    系统处于未知状态

机制

  • 通过 HealthIndicatorRegistry 收集信息
  • 通过这个里面每个HealthIndicator 实现具体检查逻辑

配置项

  • management.health.defaults.enabled=true|false
    默认打开所有的Health Indicator
  • management.health.<id>.enabled=true
    单独打开名为id的Health Indicator
  • management.endpoint.health.show-details=never|whenauthorized|always
    never:永远不显示health信息
    whenauthorized:登录之后 显示
    always :一直显示

在这里插入图片描述

以DataSourceHealthIndicator为例(检测数据库类型及其连接状态)

public class DataSourceHealthIndicator extends AbstractHealthIndicator implements InitializingBean {
    private static final String DEFAULT_QUERY = "SELECT 1";
    private DataSource dataSource;
    private String query;
    private JdbcTemplate jdbcTemplate;

    public DataSourceHealthIndicator() {
        this((DataSource)null, (String)null);
    }

    public DataSourceHealthIndicator(DataSource dataSource) { //在传入的时候 构造一个DataSource 并保存在内部
        this(dataSource, (String)null);
    }

    public DataSourceHealthIndicator(DataSource dataSource, String query) {
        super("DataSource health check failed");
        this.dataSource = dataSource;
        this.query = query;
        this.jdbcTemplate = dataSource != null ? new JdbcTemplate(dataSource) : null;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.state(this.dataSource != null, "DataSource for DataSourceHealthIndicator must be specified");
    }

    protected void doHealthCheck(Builder builder) throws Exception {
        if (this.dataSource == null) { //健康检查 如果没有DataSource 就返回一个"database", "unknown" 状况为up的  状态为健康的
            builder.up().withDetail("database", "unknown");
        } else {
            this.doDataSourceHealthCheck(builder);
        }

    }

    private void doDataSourceHealthCheck(Builder builder) throws Exception {
        String product = this.getProduct(); //如果有database在这里 就做一个尝试 将它是什么数据库取出来
        builder.up().withDetail("database", product); //将它赋给database
        String validationQuery = this.getValidationQuery(product);//根据数据库 取出校验语句
        if (StringUtils.hasText(validationQuery)) {
            List<Object> results = this.jdbcTemplate.query(validationQuery, new DataSourceHealthIndicator.SingleColumnRowMapper());//根据校验语句取出结果
            Object result = DataAccessUtils.requiredSingleResult(results);//如果结果正确
            builder.withDetail("hello", result);//将结果放到hello中
        }

    }

    private String getProduct() {
        return (String)this.jdbcTemplate.execute(this::getProduct);
    }

    private String getProduct(Connection connection) throws SQLException {
        return connection.getMetaData().getDatabaseProductName();
    }

    protected String getValidationQuery(String product) {
        String query = this.query;
        if (!StringUtils.hasText(query)) {
            DatabaseDriver specific = DatabaseDriver.fromProductName(product);
            query = specific.getValidationQuery();
        }

        if (!StringUtils.hasText(query)) {
            query = "SELECT 1";
        }

        return query;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public String getQuery() {
        return this.query;
    }

    private static class SingleColumnRowMapper implements RowMapper<Object> {
        private SingleColumnRowMapper() {
        }

        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            ResultSetMetaData metaData = rs.getMetaData();
            int columns = metaData.getColumnCount();
            if (columns != 1) {
                throw new IncorrectResultSetColumnCountException(1, columns);
            } else {
                return JdbcUtils.getResultSetValue(rs, 1);
            }
        }
    }
}

以DiskSpaceHealthIndicator为例(检测磁盘空间)

public class DiskSpaceHealthIndicator extends AbstractHealthIndicator {
    private static final Log logger = LogFactory.getLog(DiskSpaceHealthIndicator.class);
    private final File path;
    private final DataSize threshold;

    public DiskSpaceHealthIndicator(File path, DataSize threshold) {
        super("DiskSpace health check failed");
        this.path = path;
        this.threshold = threshold;
    }

    /** @deprecated */
    @Deprecated
    public DiskSpaceHealthIndicator(File path, long threshold) {
        this(path, DataSize.ofBytes(threshold));
    }

    protected void doHealthCheck(Builder builder) throws Exception {
        long diskFreeInBytes = this.path.getUsableSpace();//将这个路径下可用的磁盘空间取出
        if (diskFreeInBytes >= this.threshold.toBytes()) {//如果可用空间大于预值
            builder.up(); //健康状况就是好的
        } else {
            logger.warn(String.format("Free disk space below threshold. Available: %d bytes (threshold: %s)", diskFreeInBytes, this.threshold));
            builder.down();//反之 健康状况就是不好的
        }

        builder.withDetail("total", this.path.getTotalSpace()).withDetail("free", diskFreeInBytes).withDetail("threshold", this.threshold.toBytes());
    }
}

自定义 Health Indicator

方法

  • 实现 HealthIndicator 接口
  • 根据自定义检查逻辑返回对应 Health 状态
    • Health 中包含状态和详细描述信息

例子

在这里插入图片描述
CoffeeIndicator

@Component
public class CoffeeIndicator implements HealthIndicator {
    @Autowired
    private CoffeeService coffeeService;

    @Override
    public Health health() {
        long count = coffeeService.getCoffeeCount();//每次检查的时候 取出Count
        Health health;
        if (count > 0) {
            health = Health.up()  //如果coffee数量大于0 表明 系统的健康状况是好的
                    .withDetail("count", count)
                    .withDetail("message", "We have enough coffee.")
                    .build();
        } else {
            health = Health.down()
                    .withDetail("count", 0)
                    .withDetail("message", "We are out of coffee.")
                    .build();
        }
        return health;
    }
}

application.properties

spring.jpa.hibernate.ddl-auto=none
#ddl-auto 的几个常用属性值:
#none:默认值,什么都不做,每次启动项目,不会对数据库进行任何验证和操作
#create:每次运行项目,没有表会新建表,如果表内有数据会被清空
#create-drop:每次程序结束的时候会清空表
#update:每次运行程序,没有表会新建表,但是表内有数据不会被清空,只会更新表结构。
#validate:运行程序会校验数据与数据库的字段类型是否相同,不同会报错

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true 
# 格式化sql语句
pring.jpa.properties.hibernate.use_sql_comments=true
#指出是什么操作生成了该语句

management.endpoints.web.exposure.include=*
#在web接口开启所有的endpoint
management.endpoint.health.show-details=always
#永远显示健康状况

info..app.name=coffee
info.app.author=Long
#info.app.encoding=@project.build.sourceEncoding@
info.app.encoding=UTF-8
#通过设置Spring属性info.*,你可以定义info端点暴露的数据。所有在info关键字下的Environment属性都将被自动暴露

pom

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- 增加actuator开启依赖 -->
		<dependency>
			<groupId>io.micrometer</groupId>
			<artifactId>micrometer-registry-prometheus</artifactId>
		</dependency>
		<!-- 增加 prometheus 依赖 否则 运行prometheus无效 -->
		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.jadira.usertype</groupId>
			<artifactId>usertype.core</artifactId>
			<version>6.0.1.GA</version>
		</dependency>
		<!-- 增加Jackson的Hibernate类型支持 -->
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-hibernate5</artifactId>
			<version>2.9.8</version>
		</dependency>
		<!-- 增加Jackson XML支持 -->
		<!--<dependency>-->
			<!--<groupId>com.fasterxml.jackson.dataformat</groupId>-->
			<!--<artifactId>jackson-dataformat-xml</artifactId>-->
			<!--<version>2.9.0</version>-->
		<!--</dependency>-->

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

结果

打开 http://localhost:8080/actuator/
在这里插入图片描述

可以看到 我们能检测到的所有可以检测到的数据

其中,使用http://localhost:8080/actuator/info查看我们在application.properties中设置的info信息
在这里插入图片描述
使用http://localhost:8080/actuator/health,查看程序健康状况
在这里插入图片描述
第一个是咖啡的数量及其状态
第二个是使用的数据库及其状态
第三个是磁盘的使用空间及其状态
三个状态都为up时 这个系统的状态就为 否则 为down

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值