一、了解Actuator
为了在Springboot 应用中启用Actuator,需要在构建文件中添加Actuator starter依赖。在pom.xml添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加完成后,应用就具有以下可用Actuator端口:
HTTP方法 | 路径 | 描述 | 默认是否启用 |
---|---|---|---|
GET | /auditevents | 生成所有已经触发的审计的报告 | 否 |
GET | /beans | 描述Spring应用程序上下文中的所有bean | 否 |
GET | /conditions | 生成自动配置条件通过或者失败的报告,会指导应用上下文中bean的创建 | 否 |
GET | /configprops | 描述所有配置属性以及当前值 | 否 |
GET, POST, DELETE | /env | 生成Spring应用可用的所有属性源及其属性的报告 | 否 |
GET | /env/{toMatch} | 描述某个环境属性的值 | 否 |
GET | /health | 返回聚合的应用健康状态,可能的话,还会包含外部依赖应用的健康状态 | 是 |
GET | /heapdump | 下载堆dump文件 | 否 |
GET | /httptrace | 生成最近100个请求的跟踪结果 | 否 |
GET | /info | 返回有关开发人员定义的关于该应用的信息 | 是 |
GET | /loggers | 生成应用中源码的包列表,其中会包含配置的以及生效的日志级别 | 否 |
GET, POST | /loggers/{name} | 返回指定logger配置的和生效的日志级别,生效的日志级别可以使用POST请求修改 | 否 |
GET | /mappings | 生成所有HTTP映射及其对应处理器方法的报告 | 否 |
GET | /metrics | 返回所有指标分类的列表 | 否 |
GET | /metrics/{name} | 返回给定指标的多纬度值集 | 否 |
GET | /scheduledtasks | 列出所有的调度任务 | 否 |
GET | /threaddump | 返回所有应用线程的报告 | 否 |
除了基于Http的端点之外,除/headdump的其他端点都以JMX MBean的形式对外暴露。
1、配置Actuator的基础路径
Actuator的前缀可以通过设置management.endpoint.web.base-path属性来修改。
如修改前缀为/management:
management:
endpoints:
web:
base-path: /management
2、启用和禁用Actuator端点
Actuator默认情况下,只有/info和/heath端点是启用的。因为Actuator本身没有保护,所以大多数端点默认是禁用的,需要我们来选择对外暴露哪些端点。
通过这2个属性management.endpoints.web.exposure.include和management.endpoints.web.exposure.exclude。
通过management.endpoints.web.exposure.include属性,可以指定哪些端点想要暴露出来。
如添加以下配置:
management:
endpoints:
web:
exposure:
include: health,info,beans,conditions
management.endpoints.web.exposure.include可以接受星号(*)作为通配符,表明所有的Actuator端点都会对外暴露,如:
management:
endpoints:
web:
exposure:
include: '*'
如果除了个别端点之外,想暴露其他的所有端点,可以明确排除一部分,如,想排除"/threaddump"和/heapdump之外的端点。
management.endpoints.web.exposure.include和management.endpoints.web.exposure.exclude属性:
management:
endpoints:
web:
exposure:
include: '*'
exclude: threaddump,heapdump
注:还可以结合Spring Security 来限制对其他端点的访问。
二、消费Actuator端点
作为HTTP端点,这些可以像任何REST API一样被消费,选择任意的HTTP客户端,包括Spring的RestTemplate和WebClient,来自基于浏览器的JavaScript应用以及简单地使用curl命令行客户端。
向Actuator发送一个GET请求。借助curl工具:
$ curl http://localhost:8081/actuator
返回:
{
"_links": {
"self": {
"href": "http://localhost:8081/actuator",
"templated": false
},
"auditevents": {
"href": "http://localhost:8081/actuator/auditevents",
"templated": false
},
"beans": {
"href": "http://localhost:8081/actuator/beans",
"templated": false
},
"health": {
"href": "http://localhost:8081/actuator/health",
"templated": false
},
// ...
}
}
1、获取应用的基础信息
$ curl localhost:8081/actuator/info
返回:
{}
1)请求关于应用的信息
可以在application.yml文件中配置info如下的属性:
info:
contact:
email: myemail@demowebsite.com
phone: xxxxxxxx
再次curl请求:
curl http://localhost:8081/actuator/info
{
"contact": {
"email": "myemail@demowebsite.com",
"phone": "xxxxxxxx"
}
}
2)查看应用的健康状况
$ curl localhost:8081/actuator/health
返回:
{"status":"UP"}
UP:外部系统是启动并且可以访问
DOWN:外部系统已经停机或者不可访问
UNKNOWN:外部系统的状态尚不清楚
OUT_OF_SERVICE:外部系统是可访问的,但目前是不可用的
默认情况,只包含聚合的状态。
可以配置management.endpoint.health.show-details属性,展示完整细节,
application.yml:
management:
endpoint:
health:
show-details: always
SpringBoot还为多个外包数据库和系统提供了健康指示器,
Cassandra
Config Server
Couchbase
Eureka
Hystrix
JDBC data sources
Elasticsearch
InfluxDB
JMS message brokers
LDAP
Email servers
Neo4j
Rabbit message brokers
Redis
Solr
2、查看配置细节
1)获取bean的装配报告
/beans端点,返回的JSON文档描述了应用上下文中的每个bean,包括它的java类型以及被注入的其他bean。
如:
$ curl localhost:8081/actuator/beans
2)自动装配
/conditions端点的自动装配分为3部分: positive matches,negative matches,unconditional classes。
如:
$ curl localhost:8081/actuator/conditions
3)查看环境和配置属性
/env应用中发挥作用的属性源
如:
$ curl localhost:8081/actuator/env
还可以获取特定的属性:
如 /env/server.port:
$ curl localhost:8081/actuator/env/server.port
/env 不仅能读取属性的值,还可以向/env端点发送POST请求,如同时提交JSON文档格式的name和value字段,为正在运行的应用设置属性。
$ curl localhost:8081/actuator/env -d'{"name":"websitedemo.discount.code","value":"demo123123"}' -H "Content-type: application/json"
响应返回:
{
"websitedemo.discount.code": "demo123123"
}
再发送请求查看/env:
$ curl localhost:8081/actuator/env
可以看到多了新增的属性。
发送delete请求可以删除属性:
$ curl localhost:8081/actuator/env -X DELETE {"websitedemo.discount.code": "demo123123"}
再查看/env,返回中已经没有了删除的属性。
4)HTTP映射导览
/mappings端点为应用中所有HTTP请求处理器提供了一个一站式的视图。不管是来自SpringMVC还是Actuator端点。
$ curl localhost:8081/actuator/mappings
5)管理日志级别
/loggers端点 发送get请求
$ curl localhost:8081/actuator/loggers
还可以查看指定包下的日志级别:
$ curl localhost:8081/actuator/loggers/com.example.mywesitedemo
处理返回应用程序中包的日志级别外,通过/loggers端点发送POST请求来修改已配置的日志级别。
$ curl localhost:8081/actuator/loggers/mywesitedemo/ingredients -d'{"configuredLevel":"DEBUG"}' -H"Content-type: application/json"
3、查看应用的活动
/httptrace, /threaddump,/heapdump端点。
1)跟踪HTTP活动
/httptrace端点
$ curl http://localhost:8081/actuator/httptrace
2)监控线程
/threaddump端点
$ curl http://localhost:8081/actuator/threaddump
4、获取应用的指标
/metrics端点
查看
$ curl http://localhost:8081/actuator/metrics
发送请求到/metrics/http.server.requests
$ curl http://localhost:8081/actuator/metrics/http.server.requests
返回:指标分类详情。
使用availableTags列出标签进一步细化结果。
比如请求HTTP 404的请求有多少:借助status标签
$ curl http://localhost:8081/actuator/metrics/http.server.requests?tag=status:404
又想知道有多少HTTP404响应式发送到/**路径的,通过进一步过滤即可:
$ curl http://localhost:8081/actuator/metrics/http.server.requests?tag=status:404&tag=uri:/**
三、自定义Actuator
1、为/info 端点提供信息
1)创建自定义的InfoContributor
package tacos.tacos;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.info.Info.Builder;
@Component
public class TacoCountInfoContributor implements InfoContributor {
private TacoRepository tacoRepo;
public TacoCountInfoContributor(TacoRepository tacoRepo) {
this.tacoRepo = tacoRepo;
}
@Override
public void contribute(Builder builder) {
long tacoCount = tacoRepo.count();
Map<String, Object> tacoMap = new HashMap<String, Object>();
tacoMap.put("count", tacoCount);
builder.withDetail("taco-stats", tacoMap);
}
}
访问:
$ curl http://localhost:8081/actuator/info
返回响应:
{
"taco-stats": {
"count": 44
}
}
2)注入构建信息“/info”端点中
3)暴露Git提交信息
<build>
<plugins>
<!-- ...-->
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>
2、实现自定义的健康指示器
创建一个实现了HealthIndicator接口的bean。
如:
package com.example.myhealth ;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import java.util.Calendar;
@Component
public class WackoHealthIndicator implements HealthIndicator {
@Override
public Health health() {
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour > 12) {
return Health
.outOfService()
.withDetail("reason",
"I'm out of service after lunchtime")
.withDetail("hour", hour)
.build();
}
if (Math.random() < 0.1) {
return Health
.down()
.withDetail("reason", "I break 10% of the time")
.build();
}
return Health
.up()
.withDetail("reason", "All is good!")
.build();
}
}
当对外外部系统发起了一个远程调用,并给予接收到的响应状态进行判断,这样就是一个非常有用的健康指示器了。
3、注册自定义的指标
/metrics端点
package tacos.tacos;
import java.util.List;
import org.springframework.data.rest.core.event.AbstractRepositoryEventListener;
import org.springframework.stereotype.Component;
import io.micrometer.core.instrument.MeterRegistry;
@Component
public class TacoMetrics extends AbstractRepositoryEventListener<Taco> {
private MeterRegistry meterRegistry;
public TacoMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
protected void onAfterCreate(Taco taco) {
List<Ingredient> ingredients = taco.getIngredients();
for (Ingredient ingredient : ingredients) {
meterRegistry.counter("tacocloud",
"ingredient", ingredient.getId()).increment();
}
}
}
TacoMetrics 通过构造器注入了MeterRegistry ,扩展了AbstractRepositoryEventListener,这是Spring Data中的一个类,能够拦截repository事件。
这里重写了onAfterCreate方法,保证新的Taco对象保存都会得到通知。
查询/metrics端点来获取计数信息。
4、创建自定义的端点
通过添加@Endpoint注解来实现。
Actuator端点的操作是通过为方法添加@ReadOperation, @WriteOperation和 @DeleteOperation注解实现的。
允许Actuator与各种各样的通信机制协作,内置了对HTTP和JMX的支持。
自定义一个端点如:
package com.example.myendpoint;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
@Endpoint(id="notes", enableByDefault=true)
public class NotesEndpoint {
private List<Note> notes = new ArrayList<>();
@ReadOperation
public List<Note> notes() {
return notes;
}
@WriteOperation
public List<Note> addNote(String text) {
notes.add(new Note(text));
return notes;
}
@DeleteOperation
public List<Note> deleteNote(int index) {
if (index < notes.size()) {
notes.remove(index);
}
return notes;
}
@RequiredArgsConstructor
private class Note {
@Getter
private Date time = new Date();
@Getter
private final String text;
}
}
在application.yml添加notes端点:
management:
endpoints:
web:
exposure:
include: notes
访问:
$ curl http://localhost:8081/actuator/notes -d'{"text":"Bring home milk"}' -H"Content-type: application/json
返回:
[{"time":"2018-06-08T06:21:01.085+0000","text":"Bring home milk"}]
删除其中一个,发送delete请求。并将index作为请求参数。
$ curl localhost:8081/actuator/notes?index=1 -X DELETE
它们会暴露MBean,可以使用任意的JMX客户端来进行访问。如果只想暴露HTTP端点,可以使用@WebEndpoint注解而不是 @Endpoint来标注端点类。
@Component
@WebEndpoint(id="notes", enableByDefault=true)
public class NotesEndpoint {
//...
}
四、保护Actuator
可以使用Spring Security来保护Actuator
如,只有ROLE_ADMIN权限的用户才能调用Actuator端点。
重写WebSecurityConfigurerAdapter的configure()方法:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
这样就只有ROLE_ADMIN权限的授权用户才能访问。现在端点是硬编码“/actuator/**”,如果修改了management.endpoints.web.base-path属性,这种方式就无法正常运行了。
SpringBoot提供了EndpointRequest(一个请求匹配器类,它可以使其更容易且更少地依赖于给定的字符串路径)。
修改上面代码如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
EndpointRequest.toAnyEndpoint()返回一个请求匹配器,它会匹配所有的Actuator端点。
如果想要将某些端点从请求匹配中移除,可以调用excluding()方法,通过名称进行声明。
如:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint()
.excluding("health", "info"))
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
如果只是想将安全应用到一部分Actuator端点中,可以调用to()来替换toAnyEndpoint()方法,并使用名称指明这些端点,如:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.to(
"beans", "threaddump", "loggers"))
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
这样只会将安全功能应用到 /beans, /threaddump, 和/loggers 端点上。