MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java编程中的Spring Boot Actuator使用

2022-02-105.5k 阅读

一、Spring Boot Actuator 简介

Spring Boot Actuator 是 Spring Boot 提供的一个强大的功能模块,它为 Spring Boot 应用程序添加了生产就绪(production - ready)特性,使得我们可以在应用运行时对其进行监控和管理。这些特性包括健康检查、指标收集、环境信息查看、线程转储、审计等,大大方便了开发人员在开发、测试以及生产环境中对应用程序的调试和维护。

在传统的 Java Web 应用开发中,要实现对应用状态的监控和管理,通常需要手动集成各种不同的工具和框架,这是一个复杂且耗时的过程。而 Spring Boot Actuator 基于 Spring Boot 的自动配置和约定大于配置的理念,将这些功能集成在一起,并且提供了统一且简洁的接口,让开发人员可以轻松地为应用程序添加这些关键功能。

二、Spring Boot Actuator 的依赖引入

在使用 Spring Boot Actuator 之前,我们需要在项目中引入相应的依赖。如果使用的是 Maven 构建工具,在 pom.xml 文件中添加如下依赖:

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

如果使用 Gradle 构建工具,在 build.gradle 文件中添加:

implementation 'org.springframework.boot:spring-boot-starter-actuator'

引入依赖后,Spring Boot 会自动配置 Actuator 的相关端点(endpoints),这些端点就是我们与 Actuator 交互,获取应用程序运行时信息的接口。

三、Actuator 端点详解

  1. 健康检查端点(/actuator/health)
    • 功能:健康检查端点用于检查应用程序的健康状况。它会聚合多个健康指标检查器(health indicators)的结果,返回应用程序是否健康的总体状态。常见的健康指标检查包括数据库连接是否正常、外部服务是否可达等。
    • 示例:假设我们的应用程序连接了一个 MySQL 数据库,Actuator 会自动配置一个 DataSourceHealthIndicator 来检查数据库连接的健康状况。当我们访问 /actuator/health 端点时,如果数据库连接正常,返回结果类似如下:
{
    "status": "UP",
    "components": {
        "db": {
            "status": "UP",
            "details": {
                "database": "MySQL",
                "hello": 1
            }
        }
    }
}

如果数据库连接出现问题,status 可能会变为 DOWN,并且在 details 中会包含具体的错误信息,帮助我们快速定位问题。 - 自定义健康检查:除了默认的健康检查器,我们还可以自定义健康检查逻辑。例如,假设我们的应用程序依赖于一个外部 RESTful 服务,我们可以创建一个自定义的健康检查器:

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 RestTemplate restTemplate;

    public ExternalServiceHealthIndicator(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Health health() {
        try {
            // 尝试调用外部服务
            restTemplate.getForObject("http://external - service - url/status", String.class);
            return Health.up().build();
        } catch (Exception e) {
            return Health.down(e).build();
        }
    }
}

这样,在健康检查时,就会同时检查这个外部服务的状态。

  1. 指标端点(/actuator/metrics)
    • 功能:指标端点用于收集和展示应用程序的各种指标数据,如内存使用情况、CPU 使用率、HTTP 请求计数、响应时间等。这些指标数据对于了解应用程序的性能和资源使用情况非常有帮助。
    • 示例:访问 /actuator/metrics 端点,会返回所有支持的指标列表:
{
    "names": [
        "jvm.memory.committed",
        "jvm.memory.max",
        "jvm.memory.used",
        "http.server.requests"
    ]
}

如果我们想查看某个具体指标的详细信息,例如 jvm.memory.used,可以访问 /actuator/metrics/jvm.memory.used,返回结果如下:

{
    "name": "jvm.memory.used",
    "description": "The amount of used memory",
    "baseUnit": "bytes",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 123456789
        }
    ],
    "availableTags": [
        {
            "tag": "area",
            "values": [
                "heap",
                "non - heap"
            ]
        },
        {
            "tag": "id",
            "values": [
                "Compressed Class Space",
                "Metaspace",
                "PS Eden Space",
                "PS Old Gen",
                "PS Survivor Space"
            ]
        }
    ]
}

这里显示了已使用内存的字节数,并且还提供了可以进一步细分指标的标签信息。 - 自定义指标:我们也可以自定义指标来收集特定业务逻辑的相关数据。例如,假设我们有一个电商应用,想统计商品的浏览次数,可以这样定义自定义指标:

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    private final Counter productViewCounter;

    public ProductService(MeterRegistry meterRegistry) {
        this.productViewCounter = meterRegistry.counter("product.views", "category", "all");
    }

    public void viewProduct() {
        productViewCounter.increment();
    }
}

然后在商品浏览的业务逻辑中调用 viewProduct() 方法,就会统计商品的浏览次数。通过 /actuator/metrics/product.views 端点可以查看这个自定义指标的数据。

  1. 环境端点(/actuator/env)
    • 功能:环境端点用于查看应用程序当前的环境配置信息,包括系统属性、环境变量、Spring 配置文件中的属性等。这在排查配置相关问题时非常有用。
    • 示例:访问 /actuator/env 端点,会返回一个 JSON 格式的配置信息,例如:
{
    "activeProfiles": [
        "prod"
    ],
    "propertySources": [
        {
            "name": "systemProperties",
            "source": {
                "java.version": "11.0.12",
                "java.home": "/usr/lib/jvm/java - 11 - openjdk - amd64",
                "user.name": "user",
                "user.home": "/home/user"
            }
        },
        {
            "name": "systemEnvironment",
            "source": {
                "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG": "en_US.UTF - 8"
            }
        },
        {
            "name": "applicationConfig: [classpath:/application - prod.properties]",
            "source": {
                "spring.datasource.url": "jdbc:mysql://localhost:3306/mydb",
                "spring.datasource.username": "root",
                "spring.datasource.password": "password"
            }
        }
    ]
}

这里展示了活动的配置文件、系统属性、环境变量以及应用程序特定的配置属性。

  1. 线程转储端点(/actuator/threaddump)
    • 功能:线程转储端点用于获取应用程序当前的线程信息,包括线程状态、堆栈跟踪等。当应用程序出现性能问题或死锁时,通过线程转储可以分析线程的执行情况,找出问题所在。
    • 示例:访问 /actuator/threaddump 端点,会返回类似如下的文本信息:
"http - nio - 8080 - exec - 1" #24 daemon prio=5 os_prio=0 tid=0x00007f8d04019000 nid=0x3b4 runnable [0x00007f8d1747f000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.NioSocketImpl.accept(Native Method)
        at sun.nio.ch.NioServerSocketChannelImpl.accept(NioServerSocketChannelImpl.java:145)
        at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:741)
        at java.lang.Thread.run(Thread.java:834)

这里详细列出了每个线程的名称、优先级、状态以及堆栈跟踪信息。

  1. 审计端点(/actuator/auditevents)
    • 功能:审计端点用于记录和展示应用程序的审计事件,例如用户登录、权限变更等重要操作。这些审计信息对于安全审计和问题追溯非常关键。
    • 示例:要使用审计端点,首先需要配置审计事件发布器。例如,使用 Spring Security 时,可以这样配置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.rememberme.jdbc.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .authorizeRequests()
               .antMatchers("/", "/home").permitAll()
               .anyRequest().authenticated()
               .and()
           .formLogin()
               .loginPage("/login")
               .permitAll()
               .and()
           .logout()
               .permitAll();

        http
           .authenticationEventPublisher(authenticationEventPublisher());
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }

    @Bean
    public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy());
    }

    @Bean
    public RequestCache requestCache() {
        return new HttpSessionRequestCache();
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        SecurityContextLogoutHandler securityContextLogoutHandler = new SecurityContextLogoutHandler();
        List<RequestMatcher> requestMatchers = new ArrayList<>();
        requestMatchers.add(new AntPathRequestMatcher("/logout"));
        requestMatchers.add(new OrRequestMatcher());
        securityContextLogoutHandler.setLogoutRequestMatcher(new OrRequestMatcher(requestMatchers));
        return securityContextLogoutHandler;
    }
}

然后,当用户登录、注销等操作发生时,相关的审计事件会被记录。访问 /actuator/auditevents 端点可以查看这些审计事件的信息,例如:

[
    {
        "principal": "user",
        "type": "AUTHENTICATION_SUCCESS",
        "timestamp": "2023 - 01 - 01T12:00:00.000Z",
        "data": {
            "details": {
                "remoteAddress": "192.168.1.100",
                "sessionId": "1234567890abcdef"
            }
        }
    }
]

这里记录了用户 user 的成功登录事件,以及相关的详细信息。

四、Actuator 端点的安全性配置

由于 Actuator 端点包含了应用程序的敏感信息,如配置信息、健康状况等,因此在生产环境中,必须对这些端点进行安全配置,以防止未经授权的访问。

  1. 使用 Spring Security 进行认证
    • 添加依赖:如果项目中尚未使用 Spring Security,需要添加 Spring Security 依赖。对于 Maven 项目,在 pom.xml 中添加:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

对于 Gradle 项目,在 build.gradle 中添加:

implementation 'org.springframework.boot:spring-boot-starter-security'
- **配置安全规则**:在 Spring Security 的配置类中,可以配置 Actuator 端点的访问权限。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .authorizeRequests()
               .antMatchers("/actuator/health").permitAll()
               .antMatchers("/actuator/info").permitAll()
               .antMatchers("/actuator/metrics").hasRole("ADMIN")
               .antMatchers("/actuator/env").hasRole("ADMIN")
               .anyRequest().authenticated()
               .and()
           .formLogin();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
            User.withDefaultPasswordEncoder()
               .username("user")
               .password("password")
               .roles("USER")
               .build();

        UserDetails admin =
            User.withDefaultPasswordEncoder()
               .username("admin")
               .password("adminpassword")
               .roles("ADMIN")
               .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

在这个配置中,/actuator/health/actuator/info 端点允许所有用户访问,而 /actuator/metrics/actuator/env 端点只允许具有 ADMIN 角色的用户访问。

  1. 使用 HTTP Basic 认证
    • 配置:如果使用 HTTP Basic 认证,可以在 application.properties 文件中配置用户名和密码:
spring.security.user.name=admin
spring.security.user.password=adminpassword

然后在 Spring Security 配置类中配置使用 HTTP Basic 认证:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .httpBasic()
               .and()
           .authorizeRequests()
               .antMatchers("/actuator/health").permitAll()
               .antMatchers("/actuator/info").permitAll()
               .antMatchers("/actuator/metrics").hasRole("ADMIN")
               .antMatchers("/actuator/env").hasRole("ADMIN")
               .anyRequest().authenticated();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
            User.withDefaultPasswordEncoder()
               .username("user")
               .password("password")
               .roles("USER")
               .build();

        UserDetails admin =
            User.withDefaultPasswordEncoder()
               .username("admin")
               .password("adminpassword")
               .roles("ADMIN")
               .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

这样,在访问受保护的 Actuator 端点时,会弹出 HTTP Basic 认证对话框,要求用户输入用户名和密码。

五、Actuator 端点的定制与扩展

  1. 定制端点的暴露方式
    • 默认暴露情况:在 Spring Boot 2.x 中,默认情况下,只有 healthinfo 端点是暴露的,并且是以 JSON 格式返回数据。如果要暴露其他端点,可以通过配置文件进行设置。
    • 配置暴露端点:在 application.properties 文件中,可以使用 management.endpoints.web.exposure.include 属性来指定要暴露的端点。例如,要暴露 metricsenv 端点,可以这样配置:
management.endpoints.web.exposure.include=health,info,metrics,env

如果要暴露所有端点,可以使用 *

management.endpoints.web.exposure.include=*
  1. 定制端点的路径
    • 默认路径:Actuator 端点默认的路径前缀是 /actuator,例如健康检查端点是 /actuator/health。如果想修改这个前缀,可以在 application.properties 文件中配置:
management.endpoints.web.base - path=/management

这样,所有 Actuator 端点的路径前缀就变为 /management,健康检查端点变为 /management/health。 3. 扩展端点 - 创建自定义端点:假设我们想创建一个自定义的端点,用于获取应用程序中某个特定业务对象的状态。首先创建一个类,实现 Endpoint 接口:

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;

@Component
@Endpoint(id = "custom - endpoint")
public class CustomEndpoint {

    @ReadOperation
    public String getCustomStatus() {
        // 这里编写获取特定业务对象状态的逻辑
        return "Custom status is OK";
    }
}

然后,通过配置暴露这个自定义端点:

management.endpoints.web.exposure.include=health,info,custom - endpoint

访问 /actuator/custom - endpoint 就可以获取自定义端点返回的信息。

六、在生产环境中使用 Actuator

  1. 监控与报警
    • 结合监控工具:可以将 Actuator 与 Prometheus 和 Grafana 等监控工具结合使用。首先,引入 Micrometer Prometheus 依赖:
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer - registry - prometheus</artifactId>
</dependency>

然后配置 Prometheus 抓取 Actuator 暴露的指标数据。在 Prometheus 的配置文件 prometheus.yml 中添加如下配置:

scrape_configs:
  - job_name: 'spring - boot - actuator'
    static_configs:
      - targets: ['your - app - host:your - app - port']
    metrics_path: '/actuator/prometheus'

这样,Prometheus 就会定期从 Actuator 的 /actuator/prometheus 端点抓取指标数据。然后可以在 Grafana 中创建仪表盘,展示这些指标数据,实现对应用程序的实时监控。 - 设置报警规则:基于 Prometheus 收集的数据,可以在 Grafana 或其他报警工具(如 Alertmanager)中设置报警规则。例如,当应用程序的内存使用率超过 80% 时发送报警邮件。 2. 故障排查 - 利用健康检查和线程转储:在生产环境中,当应用程序出现故障时,首先可以通过健康检查端点确定应用程序的整体健康状况,快速定位是哪个组件出现问题。如果怀疑是线程死锁或性能问题,可以使用线程转储端点获取线程信息,分析线程的执行情况,找出问题所在。 - 查看环境和配置信息:如果应用程序出现配置相关的问题,通过环境端点查看当前应用程序的配置信息,对比预期的配置,找出配置错误。

通过以上对 Spring Boot Actuator 的详细介绍、代码示例以及在生产环境中的应用说明,相信开发者能够熟练掌握并运用 Actuator 为自己的 Spring Boot 应用程序添加强大的监控和管理功能,提升应用程序的可靠性和可维护性。