actuator
是spring boot
项目中非常强大一个功能,有助于对应用程序进行监视和管理,通过 restful api
请求来监管、审计、收集应用的运行情况,针对微服务而言它是必不可少的一个环节
Endpoints
actuator
的核心部分,它用来监视应用程序及交互,spring-boot-actuator
中已经内置了非常多的 Endpoints(health
、info
、beans
、httptrace
、shutdown
等等),同时也允许我们自己扩展自己的端点
Spring Boot 2.0
中的端点和之前的版本有较大不同,使用时需注意。另外端点的监控机制也有很大不同,启用了不代表可以直接访问,还需要将其暴露出来,传统的management.security
管理已被标记为不推荐。
内置Endpoints
id | desc | Sensitive |
---|---|---|
auditevents | 显示当前应用程序的审计事件信息 | Yes |
bean | 显示应用Spring Beans的完整列表 | Yes |
caches | 显示可用缓存信息 | Yes |
conditions | 显示自动装配类的状态及及应用信息 | Yes |
configprops | 显示所有 @ConfigurationProperties 列表 | Yes |
env | 显示 ConfigurableEnvironment 中的属性 | Yes |
flyway | 显示 Flyway 数据库迁移信息 | Yes |
health | 显示应用的健康信息(未认证只显示status,认证显示全部信息详情) | No |
info | 显示任意的应用信息(在资源文件写info.xxx即可) | No |
liquibase | 展示Liquibase 数据库迁移 | Yes |
metrics | 展示当前应用的 metrics 信息 | Yes |
mappings | 显示所有 @RequestMapping 路径集列表 | Yes |
scheduledtasks | 显示应用程序中的计划任务 | Yes |
sessions | 允许从Spring会话支持的会话存储中检索和删除用户会话。 | Yes |
shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | Yes |
threaddump | 执行一个线程dump | Yes |
httptrace | 显示HTTP跟踪信息(默认显示最后100个HTTP请求 - 响应交换) | Yes |
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
spring-boot-maven-plugin 添加如下配置:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
属性配置
在 application.properties
文件中配置actuator
的相关配置,其中info
开头的属性,就是访问info端点中显示的相关内容,值得注意的是Spring Boot2.x
中,默认只开放了info
、health
两个端点,剩余的需要自己通过配置management.endpoints.web.exposure.include
属性来加载(有include自然就有exclude,不做详细概述了)。如果想单独操作某个端点可以使用management.endpoint.端点.enabled
属性进行启用或禁用
# 描述信息
info.blog-url=http://blog.battcn.com
info.author=Levin
info.version=@project.version@
# 加载所有的端点/默认只加载了 info / health
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# 可以关闭制定的端点
management.endpoint.shutdown.enabled=false
# 路径映射,将 health 路径映射成 rest_health 那么在访问 health 路径将为404,因为原路径已经变成 rest_health 了,一般情况下不建议使用
# management.endpoints.web.path-mapping.health=rest_health
默认装配 HealthIndicators
下列是依赖spring-boot-xxx-starter
后相关HealthIndicator
的实现(通过management.health.defaults.enabled
属性可以禁用它们),但想要获取一些额外的信息时,自定义的作用就体现出来了…
名称 | 描述 |
---|---|
CassandraHealthIndicator | 检查 Cassandra 数据库是否启动。 |
DiskSpaceHealthIndicator | 检查磁盘空间不足。 |
DataSourceHealthIndicator | 检查是否可以获得连接 DataSource 。 |
ElasticsearchHealthIndicator | 检查 Elasticsearch 集群是否启动。 |
InfluxDbHealthIndicator | 检查 InfluxDB 服务器是否启动。 |
JmsHealthIndicator | 检查 JMS 代理是否启动。 |
MailHealthIndicator | 检查邮件服务器是否启动。 |
MongoHealthIndicator | 检查 Mongo 数据库是否启动。 |
Neo4jHealthIndicator | 检查 Neo4j 服务器是否启动。 |
RabbitHealthIndicator | 检查 Rabbit 服务器是否启动。 |
RedisHealthIndicator | 检查 Redi 服务器是否启动。 |
SolrHealthIndicator | 检查 Solr 服务器是否已启动。 |
健康端点(第一种方式)
实现HealthIndicator
接口,根据自己的需要判断返回的状态是UP
还是DOWN
,功能简单。
@Component("MyHealthIndicator1")
public class MyHealthIndicator implements HealthIndicator {
private static final String VERSION = "v1.0.0";
@Override
public Health health() {
int code = check();
if (code != 0) {
Health.down().withDetail("code", code).withDetail("version", VERSION).build();
}
return Health.up().withDetail("code", code).withDetail("version", VERSION).up().build();
}
private int check() {
return 0;
}
}
健康端点(第二种方式)
继承AbstractHealthIndicator
抽象类,重写doHealthCheck
方法,功能比第一种要强大一点点,默认的DataSourceHealthIndicator
、 RedisHealthIndicator
都是这种写法,内容回调中还做了异常的处理。
@Component("MyAbstractHealthIndicator1")
public class MyAbstractHealthIndicator extends AbstractHealthIndicator {
private static final String VERSION = "v1.0.0";
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
int code = check();
if (code != 0) {
builder.down().withDetail("code", code).withDetail("version", VERSION).build();
}
builder.withDetail("code", code)
.withDetail("version", VERSION).up().build();
}
private int check() {
return 0;
}
}
自定义端点
上面介绍的 info
、health
都是spring-boot-actuator
内置的,真正要实现自己的端点还得通过@Endpoint
、 @ReadOperation
、@WriteOperation
、@DeleteOperation
。
注解介绍
不同请求的操作,调用时缺少必需参数,或者使用无法转换为所需类型的参数,则不会调用操作方法,响应状态将为400(错误请求)
@Endpoint
构建 rest api 的唯一路径@ReadOperation GET
请求,响应状态为 200 如果没有返回值响应 404(资源未找到)@WriteOperation POST
请求,响应状态为 200 如果没有返回值响应 204(无响应内容)@DeleteOperation DELETE
请求,响应状态为 200 如果没有返回值响应 204(无响应内容)
@Endpoint(id = "rtime")
public class MyEndPoint {
@ReadOperation
public Map<String, String> hello() {
Map<String, String> result = new HashMap<>();
result.put("author", "rtime");
result.put("email", "xxxxx@xxx.com");
return result;
}
}
SpringBootAdmin
SBA
全称 Spring Boot Admin
是一个管理和监控 Spring Boot
应用程序的开源项目。分为admin-server
与 admin-client
两个组件,admin-server
通过采集 actuator
端点数据,显示在 spring-boot-admin-ui
上,已知的端点几乎都有进行采集,通过 spring-boot-admin
可以动态切换日志级别、导出日志、导出heapdump、监控各项指标 等等….
什么是SBA? SBA
全称 Spring Boot Admin
是一个管理和监控 Spring Boot
应用程序的开源项目。分为admin-server
与 admin-client
两个组件,admin-server
通过采集 actuator
端点数据,显示在 spring-boot-admin-ui
上,已知的端点几乎都有进行采集,通过 spring-boot-admin
可以动态切换日志级别、导出日志、导出heapdump、监控各项指标 等等….
Spring Boot Admin
在对单一应用服务监控的同时也提供了集群监控方案,支持通过eureka
、consul
、zookeeper
等注册中心的方式实现多服务监控与管理…
导入依赖
<!-- 服务端:带UI界面 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 客户端包 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 在管理界面中与 JMX-beans 进行交互所需要被依赖的 JAR -->
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
属性配置
# \u63CF\u8FF0\u4FE1\u606F
info.blog-url=http://www.rtime.xin
info.author=luis
# \u5982\u679C Maven \u63D2\u4EF6\u6CA1\u914D\u7F6E\u6B64\u5904\u8BF7\u6CE8\u91CA\u6389
info.version=@project.version@
info.name=@project.artifactId@
# \u65E5\u5FD7\u6587\u4EF6
logging.file=./target/admin-server.log
# \u52A0\u8F7D\u6240\u6709\u7684\u7AEF\u70B9/\u9ED8\u8BA4\u53EA\u52A0\u8F7D\u4E86 info / health
management.endpoints.web.exposure.include=*
# \u6BD4\u8F83\u91CD\u8981,\u9ED8\u8BA4 /actuator spring-boot-admin \u626B\u63CF\u4E0D\u5230
management.endpoints.web.base-path=/springboot
management.endpoint.health.show-details=always
spring.boot.admin.client.url=http://localhost:8080/springboot
# \u4E0D\u914D\u7F6E\u8001\u559C\u6B22\u7528\u4E3B\u673A\u540D\uFF0C\u770B\u7740\u4E0D\u8212\u670D....
spring.boot.admin.client.instance.prefer-ip=true
# \u767B\u9646\u6240\u9700\u7684\u8D26\u53F7\u5BC6\u7801
spring.security.user.name=admin
spring.security.user.password=admin
# \u4FBF\u4E8E\u5BA2\u6237\u7AEF\u53EF\u4EE5\u5728\u53D7\u4FDD\u62A4\u7684\u670D\u52A1\u5668\u4E0A\u6CE8\u518Capi
spring.boot.admin.client.username=admin
spring.boot.admin.client.password=admin
# \u4FBF\u670D\u52A1\u5668\u53EF\u4EE5\u8BBF\u95EE\u53D7\u4FDD\u62A4\u7684\u5BA2\u6237\u7AEF\u7AEF\u70B9
spring.boot.admin.client.instance.metadata.user.name=admin
spring.boot.admin.client.instance.metadata.user.password=admin
入口
@SpringBootApplication
@EnableAdminServer
public class Chapter14Application {
public static void main(String[] args) {
SpringApplication.run(Chapter14Application.class, args);
}
/**
* dev 环境加载
*/
@Profile("dev")
@Configuration
public static class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll()
.and().csrf().disable();
}
}
/**
* chapter14 环境加载
*/
@Profile("chapter14")
@Configuration
public static class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private String adminContextPath;
public SecuritySecureConfig() {
}
@Autowired
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
if(adminContextPath != null && adminContextPath.length() != 0) {
http.authorizeRequests()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf().disable();
}
}
}
}