定制化 Actuator 端点:扩展 Spring Boot 监控能力,让你的应用“妙不可言”
各位看官,大家好!今天咱们来聊聊 Spring Boot Actuator 的“高定”玩法。啥叫“高定”?就是别再只用那些官方标配的端点了,我们要自己动手,丰衣足食,打造出独一无二的监控利器,让我们的 Spring Boot 应用“妙不可言”!
Actuator,顾名思义,就是“执行者”,或者说是“驱动者”。在 Spring Boot 的世界里,它就像一位尽职尽责的管家,负责监控应用的状态、健康状况、性能指标等等。默认情况下,它提供了一堆现成的端点,比如 /health
、/metrics
、/info
等等,让我们能够窥探应用的“小秘密”。
但是,人生嘛,总要有点追求。这些默认的端点虽然好用,但有时候不够灵活,无法满足我们特定的监控需求。比如说,你想监控某个特定的业务指标,或者想查看应用的自定义配置,这时候,就需要定制 Actuator 端点了。
一、为何要定制 Actuator 端点?难道默认的不好吗?
这就好比去餐厅吃饭,菜单上的菜品虽然丰富,但总有那么一两道菜,你希望可以按照自己的口味调整一下。定制 Actuator 端点,就是为了满足我们“个性化”的监控需求。
以下是一些常见的定制场景:
- 业务指标监控: 监控订单数量、用户活跃度、支付成功率等业务相关的指标。
- 自定义配置查看: 查看应用的自定义配置,比如数据库连接池大小、缓存过期时间等。
- 特定资源状态监控: 监控外部服务的可用性、消息队列的连接状态等。
- 自定义健康检查: 根据业务逻辑判断应用是否健康,比如判断某个关键服务是否正常运行。
- 触发特定操作: 比如刷新缓存、重新加载配置等。
总而言之,定制 Actuator 端点可以让我们更深入地了解应用的运行状况,及时发现问题,并采取相应的措施。
二、定制 Actuator 端点的几种姿势
Spring Boot 提供了多种方式来定制 Actuator 端点,咱们来一一了解一下:
-
使用
@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
时,该方法会被调用,并返回一个包含message
和timestamp
的 Map 对象。重点提示:
@Endpoint
注解的id
属性必须是唯一的,否则会发生冲突。- 可以使用
@WriteOperation
注解处理 POST 请求,使用@DeleteOperation
注解处理 DELETE 请求。
-
使用
@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 端点,比如展示一些静态信息。
-
扩展现有的健康指示器 (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()
方法表示应用处于服务中断状态。
- 健康指示器会自动被 Spring Boot 注册,并显示在
-
实现
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 对象等等。
- 信息贡献器会自动被 Spring Boot 注册,并显示在
三、定制 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 应用“妙不可言”! 记住,监控不仅仅是技术,更是一种艺术,一种对应用负责的态度。祝各位编程愉快!