定制化 Actuator 端点:扩展 Spring Boot 监控能力

定制化 Actuator 端点:扩展 Spring Boot 监控能力,让你的应用“妙不可言”

各位看官,大家好!今天咱们来聊聊 Spring Boot Actuator 的“高定”玩法。啥叫“高定”?就是别再只用那些官方标配的端点了,我们要自己动手,丰衣足食,打造出独一无二的监控利器,让我们的 Spring Boot 应用“妙不可言”!

Actuator,顾名思义,就是“执行者”,或者说是“驱动者”。在 Spring Boot 的世界里,它就像一位尽职尽责的管家,负责监控应用的状态、健康状况、性能指标等等。默认情况下,它提供了一堆现成的端点,比如 /health/metrics/info 等等,让我们能够窥探应用的“小秘密”。

但是,人生嘛,总要有点追求。这些默认的端点虽然好用,但有时候不够灵活,无法满足我们特定的监控需求。比如说,你想监控某个特定的业务指标,或者想查看应用的自定义配置,这时候,就需要定制 Actuator 端点了。

一、为何要定制 Actuator 端点?难道默认的不好吗?

这就好比去餐厅吃饭,菜单上的菜品虽然丰富,但总有那么一两道菜,你希望可以按照自己的口味调整一下。定制 Actuator 端点,就是为了满足我们“个性化”的监控需求。

以下是一些常见的定制场景:

  • 业务指标监控: 监控订单数量、用户活跃度、支付成功率等业务相关的指标。
  • 自定义配置查看: 查看应用的自定义配置,比如数据库连接池大小、缓存过期时间等。
  • 特定资源状态监控: 监控外部服务的可用性、消息队列的连接状态等。
  • 自定义健康检查: 根据业务逻辑判断应用是否健康,比如判断某个关键服务是否正常运行。
  • 触发特定操作: 比如刷新缓存、重新加载配置等。

总而言之,定制 Actuator 端点可以让我们更深入地了解应用的运行状况,及时发现问题,并采取相应的措施。

二、定制 Actuator 端点的几种姿势

Spring Boot 提供了多种方式来定制 Actuator 端点,咱们来一一了解一下:

  1. 使用 @Endpoint 注解

    这是最常见,也是最灵活的一种方式。我们可以创建一个新的类,使用 @Endpoint 注解将其标记为一个 Actuator 端点,然后定义一些方法来处理不同的 HTTP 请求。

    import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
    import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
    import org.springframework.stereotype.Component;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    @Endpoint(id = "my-custom-endpoint")
    public class MyCustomEndpoint {
    
        @ReadOperation
        public Map<String, String> getMyCustomData() {
            Map<String, String> data = new HashMap<>();
            data.put("message", "Hello from my custom endpoint!");
            data.put("timestamp", String.valueOf(System.currentTimeMillis()));
            return data;
        }
    }

    这个例子中,我们创建了一个名为 MyCustomEndpoint 的类,并使用 @Endpoint(id = "my-custom-endpoint") 注解将其标记为一个 Actuator 端点。id 属性指定了端点的 ID,可以通过 /actuator/my-custom-endpoint 访问该端点。

    @ReadOperation 注解表示该方法处理 GET 请求。当访问 /actuator/my-custom-endpoint 时,该方法会被调用,并返回一个包含 messagetimestamp 的 Map 对象。

    重点提示:

    • @Endpoint 注解的 id 属性必须是唯一的,否则会发生冲突。
    • 可以使用 @WriteOperation 注解处理 POST 请求,使用 @DeleteOperation 注解处理 DELETE 请求。
  2. 使用 @WebEndpoint@RestControllerEndpoint 注解

    @WebEndpoint@RestControllerEndpoint 注解是 @Endpoint 注解的变种,它们分别用于创建基于 Web 的端点和 REST 控制器端点。

    • @WebEndpoint:创建的端点会暴露在 /actuator 路径下。
    • @RestControllerEndpoint:创建的端点可以像普通的 REST 控制器一样使用,可以使用 @GetMapping@PostMapping 等注解来处理不同的 HTTP 请求。
    import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.stereotype.Component;
    
    @Component
    @RestControllerEndpoint(id = "greeting")
    public class GreetingEndpoint {
    
        @GetMapping("/{name}")
        public ResponseEntity<String> greet(@PathVariable String name) {
            return ResponseEntity.ok("Hello, " + name + "!");
        }
    }

    这个例子中,我们创建了一个名为 GreetingEndpoint 的类,并使用 @RestControllerEndpoint(id = "greeting") 注解将其标记为一个 REST 控制器端点。@GetMapping("/{name}") 注解表示该方法处理 GET 请求,并接受一个名为 name 的路径参数。

    当访问 /actuator/greeting/{name} 时,该方法会被调用,并返回一个包含问候语的字符串。例如,访问 /actuator/greeting/World 时,会返回 "Hello, World!"。

    重点提示:

    • @RestControllerEndpoint 注解更加灵活,可以像普通的 REST 控制器一样使用,可以使用各种 Spring MVC 的注解。
    • @WebEndpoint 注解更适合创建简单的 Web 端点,比如展示一些静态信息。
  3. 扩展现有的健康指示器 (HealthIndicator)

    如果你想定制应用的健康检查逻辑,可以扩展 HealthIndicator 接口,并实现 health() 方法。

    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.actuate.health.HealthIndicator;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyCustomHealthIndicator implements HealthIndicator {
    
        @Override
        public Health health() {
            // 在这里编写你的健康检查逻辑
            boolean isHealthy = checkMyCustomService();
            if (isHealthy) {
                return Health.up().withDetail("message", "My custom service is healthy").build();
            } else {
                return Health.down().withDetail("message", "My custom service is down").build();
            }
        }
    
        private boolean checkMyCustomService() {
            // 模拟检查外部服务是否正常运行
            return Math.random() > 0.1; // 90% 的概率返回 true
        }
    }

    这个例子中,我们创建了一个名为 MyCustomHealthIndicator 的类,并实现了 HealthIndicator 接口。health() 方法中编写了自定义的健康检查逻辑。如果 checkMyCustomService() 方法返回 true,则表示应用健康,否则表示应用不健康。

    Health.up()Health.down() 方法用于构建健康状态,withDetail() 方法用于添加额外的健康信息。

    重点提示:

    • 健康指示器会自动被 Spring Boot 注册,并显示在 /actuator/health 端点中。
    • 可以使用 Health.outOfService() 方法表示应用处于服务中断状态。
  4. 实现 InfoContributor 接口

    如果你想在 /actuator/info 端点中添加自定义的信息,可以实现 InfoContributor 接口,并实现 contribute() 方法。

    import org.springframework.boot.actuate.info.Info;
    import org.springframework.boot.actuate.info.InfoContributor;
    import org.springframework.stereotype.Component;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class MyCustomInfoContributor implements InfoContributor {
    
        @Override
        public void contribute(Info.Builder builder) {
            Map<String, String> customInfo = new HashMap<>();
            customInfo.put("version", "1.0.0");
            customInfo.put("author", "Your Name");
            builder.withDetail("my-custom-info", customInfo);
        }
    }

    这个例子中,我们创建了一个名为 MyCustomInfoContributor 的类,并实现了 InfoContributor 接口。contribute() 方法中添加了自定义的信息,包括版本号和作者信息。

    重点提示:

    • 信息贡献器会自动被 Spring Boot 注册,并显示在 /actuator/info 端点中。
    • 可以使用 builder.withDetail() 方法添加各种类型的信息,比如字符串、数字、Map 对象等等。

三、定制 Actuator 端点的实战案例

光说不练假把式,咱们来几个实战案例,让大家更好地理解如何定制 Actuator 端点。

案例一:监控数据库连接池状态

假设我们使用 HikariCP 作为数据库连接池,我们想要监控连接池的活跃连接数、空闲连接数、等待连接数等指标。

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Component
@Endpoint(id = "database-pool")
public class DatabasePoolEndpoint {

    private final DataSource dataSource;

    public DatabasePoolEndpoint(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @ReadOperation
    public Map<String, Object> getDatabasePoolInfo() {
        Map<String, Object> poolInfo = new HashMap<>();
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
            poolInfo.put("activeConnections", hikariDataSource.getHikariPoolMXBean().getActiveConnections());
            poolInfo.put("idleConnections", hikariDataSource.getHikariPoolMXBean().getIdleConnections());
            poolInfo.put("threadsAwaitingConnection", hikariDataSource.getHikariPoolMXBean().getThreadsAwaitingConnection());
            poolInfo.put("totalConnections", hikariDataSource.getHikariPoolMXBean().getTotalConnections());
        } else {
            poolInfo.put("message", "Not using HikariCP as DataSource.");
        }
        return poolInfo;
    }
}

这个例子中,我们创建了一个名为 DatabasePoolEndpoint 的类,并使用 @Endpoint(id = "database-pool") 注解将其标记为一个 Actuator 端点。getDatabasePoolInfo() 方法获取 HikariCP 连接池的各种指标,并将其封装成一个 Map 对象返回。

访问 /actuator/database-pool 即可查看数据库连接池的状态。

案例二:自定义健康检查,判断外部服务是否可用

假设我们的应用依赖于一个外部服务,我们想要在健康检查中判断该服务是否可用。

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {

    private final String externalServiceUrl = "http://external-service.example.com/health"; // 替换成外部服务的地址
    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public Health health() {
        try {
            // 尝试调用外部服务的健康检查接口
            restTemplate.getForEntity(externalServiceUrl, String.class);
            return Health.up().withDetail("message", "External service is healthy").build();
        } catch (Exception e) {
            return Health.down(e).withDetail("message", "External service is down").build();
        }
    }
}

这个例子中,我们创建了一个名为 ExternalServiceHealthIndicator 的类,并实现了 HealthIndicator 接口。health() 方法中使用 RestTemplate 调用外部服务的健康检查接口,如果调用成功,则表示外部服务可用,否则表示外部服务不可用。

/actuator/health 端点会显示外部服务的健康状态。

案例三:在 /actuator/info 端点中添加 Git 信息

Spring Boot Actuator 可以自动显示 Git 信息,但前提是你的项目使用了 Git,并且配置了 spring.git.properties 文件。如果你想手动添加 Git 信息,可以这样做:

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class GitInfoContributor implements InfoContributor {

    @Override
    public void contribute(Info.Builder builder) {
        Map<String, String> gitInfo = new HashMap<>();
        gitInfo.put("commitId", "your-commit-id"); // 替换成你的 Git commit ID
        gitInfo.put("branch", "your-branch-name"); // 替换成你的 Git 分支名
        builder.withDetail("git", gitInfo);
    }
}

这个例子中,我们创建了一个名为 GitInfoContributor 的类,并实现了 InfoContributor 接口。contribute() 方法中添加了 Git 信息,包括 commit ID 和分支名。

/actuator/info 端点会显示 Git 信息。

四、Actuator 端点的安全配置

Actuator 端点默认情况下是未受保护的,这意味着任何人都可以访问这些端点,这可能会带来安全风险。因此,我们需要对 Actuator 端点进行安全配置。

Spring Security 可以很好地保护 Actuator 端点。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/actuator/**").hasRole("ACTUATOR") // 需要 ACTUATOR 角色才能访问
                .anyRequest().permitAll() // 其他请求允许访问
                .and()
                .httpBasic(); // 使用 HTTP Basic 认证
    }
}

这个例子中,我们配置了 Spring Security,要求只有具有 ACTUATOR 角色的用户才能访问 /actuator/** 路径下的端点。同时,我们使用了 HTTP Basic 认证,要求用户提供用户名和密码。

重点提示:

  • 可以根据实际需求配置不同的安全策略,比如使用 OAuth 2.0 认证、JWT 认证等等。
  • 建议对所有 Actuator 端点进行安全保护,避免敏感信息泄露。

五、总结

定制 Actuator 端点是扩展 Spring Boot 监控能力的重要手段。通过定制 Actuator 端点,我们可以更深入地了解应用的运行状况,及时发现问题,并采取相应的措施。

Spring Boot 提供了多种方式来定制 Actuator 端点,包括使用 @Endpoint 注解、@WebEndpoint@RestControllerEndpoint 注解、扩展 HealthIndicator 接口、实现 InfoContributor 接口等等。

同时,我们需要对 Actuator 端点进行安全配置,避免敏感信息泄露。

希望这篇文章能够帮助大家更好地理解如何定制 Actuator 端点,让你的 Spring Boot 应用“妙不可言”! 记住,监控不仅仅是技术,更是一种艺术,一种对应用负责的态度。祝各位编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注