Spring Boot Actuator

1、概览

本文将带你了解 Spring Boot Actuator,以及如何在 Spring Boot 和 WebFlux 中使用、配置和扩展这个监控工具,充分利用响应式编程模型。

2、Actuator 是啥?

从本质上讲,Actuator 为应用带来了生产就绪的功能。

有了 Actuator 后,监控应用程序、收集指标、了解流量或数据库状态就变得易如反掌。

这个库的主要好处是,可以获得生产级工具,而无需自己实际实现这些功能。

Actuator 主要公开应用的运行信息 - 健康状况、指标、信息、转储(dump)、环境等。它使用 HTTP 端点或 JMX Bean 与客户端进行交互。

一旦在 classpath 中添加了该依赖,多个端点就已经开箱即用。与大多数 Spring 模块一样,可以通过多种方式轻松配置或扩展它。

3、入门

首先,在 Spring Boot 项目中添加 spring-boot-actuator 依赖,以启用 Spring Boot Actuator。

Mave:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

该依赖的版本号已经在 spring-boot-starter-parent 中声明了,所以只要你继承了 Parent 项目,不需要手动声明版本号。

Actuator 与技术无关,Actuator 将其模型定义为可插拔和可扩展的,而不依赖 MVC。因此,可以利用 MVC 和 WebFlux 作为底层 Web 实现,还可以通过实现正确的适配器来添加新的实现。也支持 JMX,无需任何额外代码即可暴露端点。

4、端点配置

4.2、默认端点和安全配置

Actuator 禁用了大部分端点。因此,默认情况下只有 /health/info 这两个端点可用。

如果想启用所有端点,可以设置 management.endpoints.web.exposure.include=*。或者,也可以列出应启用的端点。

另外,Actuator 与常规应用 Security 规则共享 Security 配置,从而大大简化了 Security 模型。因此,如果项目中使用了 Spring Security,就必须调整 Actuator 的 Security 规则,以允许 Actuator 端点。

只需添加一个 /actuator/** 条目即可:

@Bean
public SecurityWebFilterChain securityWebFilterChain(
  ServerHttpSecurity http) {
    return http.authorizeExchange()
      .pathMatchers("/actuator/**").permitAll()
      .anyExchange().authenticated()
      .and().build();
}

所有 Actuator 端点都默认位于 /actuator 路径下。也可以使用配置属性 management.endpoints.web.base-path 来调整路径。

你可以在 Actuator 中文文档 中找到更多细节,

4.3、预定义的端点

Actuator 部分预定义的端点如下:

  • /auditevents 列出了与安全审计相关的事件,如用户登录/注销。此外,还可以根据 Principal 或类型等字段进行过滤。
  • /beans 返回 BeanFactory 中所有可用的 Bean。与 /auditevents 不同,它不支持过滤。
  • /conditions(之前称为 /autoconfig)会生成有关自动配置条件的报告。
  • /configprops 允许获取所有 @ConfigurationProperties Bean。
  • /env 返回当前环境属性(Environment Properties),也可以检索单个属性。
  • /flyway 提供了有关 Flyway 数据库迁移的详细信息。
  • /health 汇总了应用的健康状况。
  • /heapdump 会构建并返回应用所用 JVM 的 Heap Dump。
  • /info 返回一般信息。它可能是自定义数据、构建信息或最新提交的详细信息。
  • /liquibase 的行为类似于 /flyway,但针对的是 Liquibase。
  • /logfile 返回普通应用日志。
  • /loggers 能够查询和修改应用的日志级别。
  • /metrics 详细介绍了应用的指标。这可能包括通用指标和自定义指标。
  • /prometheus 返回的指标与前一个类似,但格式化后可与 Prometheus 服务器一起使用。
  • /scheduledtasks 提供了应用中每个计划(定时)任务的详细信息。
  • /sessions 列出了 HTTP Session,前提是正在使用 Spring Session。
  • /shutdown 可以优雅地关闭应用。
  • /threaddump 会 dump 底层 JVM 的线程信息。

4.4、Actuator 端点的 Hypermedia

Spring Boot 添加了一个发现端点,可返回所有可用 Actuator 端点的链接。这有助于发现 Actuator 端点及其相应的 URL。

默认情况下,可通过 /actuator 端点访问该发现端点,向这个 URL 发送 GET 请求,它就会返回各个端点的 Actuator 链接:

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/actuator",
      "templated": false
    },
    "features-arg0": {
      "href": "http://localhost:8080/actuator/features/{arg0}",
      "templated": true
    },
    "features": {
      "href": "http://localhost:8080/actuator/features",
      "templated": false
    },
    "beans": {
      "href": "http://localhost:8080/actuator/beans",
      "templated": false
    },
    "caches-cache": {
      "href": "http://localhost:8080/actuator/caches/{cache}",
      "templated": true
    },
    // 省略其他的 ...
}

如上所示,/actuator 端点会在 _links 字段下返回所有可用的 Actuator 端点。

如果配置了自定义 Management Base Path,则应使用该基本路径作为发现 URL。例如,如果将 management.endpoints.web.base-path 设置为 /mgmt,那么就应该请求 /mgmt 端点来查看链接列表。

注意,当 Management Base Path 设置为 / 时,发现端点会被禁用,以防止与其他映射发生冲突。

4.5、健康指标

可以通过实现 HealthIndicator 接口来轻加自定义指标。ReactiveHealthIndicator 用于实现响应式健康检查。

自定义响应式健康检查:

@Component
public class DownstreamServiceHealthIndicator implements ReactiveHealthIndicator {

    @Override
    public Mono<Health> health() {
        return checkDownstreamServiceHealth().onErrorResume(
          ex -> Mono.just(new Health.Builder().down(ex).build())
        );
    }

    private Mono<Health> checkDownstreamServiceHealth() {
        // 可以使用 WebClient 来被动地检查健康状况
        return Mono.just(new Health.Builder().up().build());
    }
}

健康指标的一个方便之处在于,可以将其作为层次结构的一部分进行汇总。

因此,按照前面的例子,可以将所有下游服务归入 downstream-services 类别。只要每个嵌套的服务都是可访问的,那么这个类别就是健康的。

4.6、监控指标分组

可以将健康指标组织成 组(Group),并对所有组员应用相同的配置。

例如,可以在 application.properties 中添加以下内容,创建一个名为 custom 的健康组:

management.endpoint.health.group.custom.include=diskSpace,ping

这样,custom 组就包含了 diskSpaceping 健康指标。

现在,如果调用 /actuator/health 端点,它就会在 JSON 响应中返回新的健康组:

{"status":"UP","groups":["custom"]}

通过健康组,可以看到一些健康指标的综合结果。

在这种情况下,向 /actuator/health/custom 发送请求,响应如下:

{"status":"UP"}

可以通过 application.properties 配置组以显示更多细节:

management.endpoint.health.group.custom.show-components=always
management.endpoint.health.group.custom.show-details=always

现在,如果向 /actuator/health/custom 发起相同的请求,就能看到更多细节:

{
  "status": "UP",
  "components": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 499963170816,
        "free": 91300069376,
        "threshold": 10485760
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

也可以只向授权用户显示这些详细信息:

management.endpoint.health.group.custom.show-components=when_authorized
management.endpoint.health.group.custom.show-details=when_authorized

还可以自定义状态映射。

例如,它可以返回 207 状态码,而不是 HTTP 200 OK 响应:

management.endpoint.health.group.custom.status.http-mapping.up=207

上述配置告诉 Spring Boot,如果 custom 组状态为 UP,则返回 207 HTTP 状态码。

4.7、指标

从 Spring Boot 2 开始,内部度量标准已被 Micrometer 支持所取代,我们需要直接与 Micrometer 进行交互。Spring Boot 会自动配置一个类型为 MeterRegistry 的 Bean。

由于 Micrometer 现在是 Actuator 依赖的一部分,所以只要 Actuator 依赖在 classpath 中,就可以使用了。

/metrics 端点获取响应:

{
  "names": [
    "jvm.gc.pause",
    "jvm.buffer.memory.used",
    "jvm.memory.used",
    "jvm.buffer.count",
    // ...
  ]
}

获取特定指标的实际值,例如 /actuator/metrics/jvm.gc.pause

{
  "name": "jvm.gc.pause",
  "measurements": [
    {
      "statistic": "Count",
      "value": 3.0
    },
    {
      "statistic": "TotalTime",
      "value": 7.9E7
    },
    {
      "statistic": "Max",
      "value": 7.9E7
    }
  ],
  "availableTags": [
    {
      "tag": "cause",
      "values": [
        "Metadata GC Threshold",
        "Allocation Failure"
      ]
    },
    {
      "tag": "action",
      "values": [
        "end of minor GC",
        "end of major GC"
      ]
    }
  ]
}

4.8、自定义 /info 端点

可以使用 git-commit-id-plugin 依赖添加 git 详细信息。

<dependency>
    <groupId>pl.project13.maven</groupId>
    <artifactId>git-commit-id-plugin</artifactId>
</dependency>

同样,也可以使用 Maven 或 Gradle 插件,在其中加入构建信息,包括名称、Group 和版本:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>

4.9、创建自定义端点

创建一个 Actuator 端点,用于在应用中查询、启用和禁用 Feature:

@Component
@Endpoint(id = "features")
public class FeaturesEndpoint {

    private Map<String, Feature> features = new ConcurrentHashMap<>();

    @ReadOperation
    public Map<String, Feature> features() {
        return features;
    }

    @ReadOperation
    public Feature feature(@Selector String name) {
        return features.get(name);
    }

    @WriteOperation
    public void configureFeature(@Selector String name, Feature feature) {
        features.put(name, feature);
    }

    @DeleteOperation
    public void deleteFeature(@Selector String name) {
        features.remove(name);
    }

    public static class Feature {
        private Boolean enabled;

        // get / set 方法
    }

}

要获得端点,需要一个 Bean。在本例中,使用 @Component 来实现这一点。此外,还需要用 @Endpoint 来装饰这个 Bean。

端点的路径由 @Endpointid 参数决定。在本例中,它会将请求路由到 /actuator/features

准备就绪后,就可以开始定义操作了:

  • @ReadOperation:映射到 HTTP GET
  • @WriteOperation:映射到 HTTP POST
  • @DeleteOperation:映射到 HTTP DELETE

Spring Boot 会在运行时注册该端点。

可以在日志中查看到:

[...].WebFluxEndpointHandlerMapping: Mapped "{[/actuator/features/{name}],
  methods=[GET],
  produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features],
  methods=[GET],
  produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features/{name}],
  methods=[POST],
  consumes=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
[...].WebFluxEndpointHandlerMapping : Mapped "{[/actuator/features/{name}],
  methods=[DELETE]}"[...]

在上述的日志中,可以看到 WebFlux 是如何暴露新端点的。如果切换到 MVC,也无需更改任何代码。

此外,这还有一些注意事项:

  • 与 MVC 没有依赖
  • 使用 @Endpoint(id = "features", enableByDefault = false) 启用或禁用端点。
  • 使用 @DeleteOperation 来定义 DELETE 操作。

4.10、扩展现有端点

假如我们想确保应用的生产实例永远不会是 SNAPSHOT 版本。

为此,我们决定改变返回该信息的 Actuator 端点(即 /info)的 HTTP 状态码。如果应用程序碰巧是 SNAPSHOT,就返回不同的 HTTP 状态码。

可以使用 @EndpointExtension 注解或其更具体的 @EndpointWebExtension@EndpointJmxExtension 轻松扩展预定义端点的行为:

@Component
@EndpointWebExtension(endpoint = InfoEndpoint.class)
public class InfoWebEndpointExtension {

    private InfoEndpoint delegate;

    // 构造函数

    @ReadOperation
    public WebEndpointResponse<Map> info() {
        Map<String, Object> info = this.delegate.info();
        Integer status = getStatus(info);
        return new WebEndpointResponse<>(info, status);
    }

    private Integer getStatus(Map<String, Object> info) {
        // 如果是 snapshot 则返回 500
        return 200;
    }
}

4.11、启用所有端点

为了通过 HTTP 访问 actuator 端点,需要同时启用和暴露它们。

默认情况下,除 /shutdown 之外的所有端点都已启用。但是,只有 /health/info 端点是公开的。

可以添加以下配置来暴露所有端点:

management.endpoints.web.exposure.include=*

也可以明确启用特定端点(如 /shutdown):

management.endpoint.shutdown.enabled=true

公开除一个端点(如 /loggers)之外的所有启用端点,

management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=loggers

5、总结

本文介绍了 Spring Boot Actuator 含义及其作用。以及如何使用、调整和扩展它。


Ref:https://www.baeldung.com/spring-boot-actuators