Spring Boot的Actuator。它提供了很多生产级的特性,比如监控和度
量Spring Boot应用程序。 Actuator的这些特性可以通过众多REST端点、远程shell和JMX获得
对于Maven项目,引入的依赖是这样的:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Actuator Web端点
主要作用,查看配置明细,运行时度量,关闭应用程序, 获取应用程序信息(如下图)
连接 Actuator 的远程 shell
Actuator另一个深入运行中应用程序内部的方式是使用远程shell。 Spring Boot集成了CRaSH,一种能嵌入任意Java应用程序的shell。
如果用Maven构建项目,你需要在pom.xml文件里添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>
通过 JMX 监控应用程序
除了REST端点和远程shell, Actuator还把它的端点以MBean的方式发布了出来,可以通过
JMX来查看和管理。使用JMX是管理Spring Boot应用程序的一个好方法,如果你已在用JMX管理
应用程序中的其他MBean,则尤其如此;Actuator的端点都发布在org.springframework.boot域下。比如,你想要查看应用程序的请求映射关系,那么可以看一下图7-6(通过JConsole查看请求映射端点)。
调整Actuator
.1.1.修改端点 ID;
每个Actuator端点都有一个ID用来决定端点的路径,比方说,/beans端点的默认ID就是beans。如果端点的路径是由ID决定的,那么可以通过修改ID来改变端点的路径。你要做的就是设置一个属性,属性名是endpoints.endpoint-id.id。我们用/shutdown端点来做个演示,它会响应发往/shutdown的POST请求。假设你想让它处理发往/kill的POST请求,可以通过如下YAML为/shutdown赋予一个新的ID,也就是新的路径:
endpoints:
shutdown:
id: kill
重命名端点、修改其路径的理由很多。最明显的理由就是,端点的命名要和团队的术语保持一致。你也可能想重命名端点,让那些熟悉默认名称的人找不到它,借此增加一些安全感。
.1.2.启用和禁用端点;
虽然Actuator的端点都很有用,但你不一定需要全部这些端点。默认情况下,所有端点(除了/shutdown)都启用。我们已经看过如何设置endpoints.shutdown.enabled为true,以此开启/shutdown端点用同样的方式,你可以禁用其他的端点,将endpoints.endpoint-id.enabled设置为false。例如,要禁用/metrics端点,你要做的就是将endpoints.metrics.enabled属性设置为false。在application.yml里做如下设置:
endpoints:
metrics:
enabled: false
.1.3.添加自定义度量信息;
从/metrics端点获得运行中应用程序的内部度量信息,包括内存、垃圾回收和线程信息。这些都是非常有用且信息量很大的度量值,但你可能还想定义自己的度量,用来捕获应用程序中的特定信息。比方说,我们想要知道用户往阅读列表里保存了多少次图书,最简单的方法就是在每次调用ReadingListController的addToReadingList()方法时增加计数器值,计数器很容易实现,但这个不断变化的总计值如何同/metrics端点发布的度量信息一起发布出来呢?
//SpringBoot Actutor支持类
package org.springframework.boot.actuate.metrics;
public interface CounterService {
void increment(String metricName);
void decrement(String metricName);
void reset(String metricName);
}
Actuator的自动配置还会配置一个GaugeService类型的Bean。该接口与CounterService
类似,能将某个值记录到特定名称的度量值里。 GaugeService看起来是这样的:
package org.springframework.boot.actuate.metrics;
public interface GaugeService {
void submit(String metricName, double value);
}
@Controller
@RequestMapping("/")
@ConfigurationProperties("amazon")
public class ReadingListController {
private CounterService counterService;
@Autowired
public ReadingListController(
ReadingListRepository readingListRepository,
AmazonProperties amazonProperties,
CounterService counterService,
GaugeService gaugeService) {
this.readingListRepository = readingListRepository;
this.amazonProperties = amazonProperties;
this.counterService = counterService;
this.gaugeService = gaugeService;
}
...
@RequestMapping(method=RequestMethod.POST)
public String addToReadingList(Reader reader, Book book) {
book.setReader(reader);
readingListRepository.save(book);
counterService.increment("books.saved");
gaugeService.submit("books.last.saved", System.currentTimeMillis());
return "redirect:/";
}
}
尽管CounterService和GaugeService用起来很简单,但还是有一些度量值很难通过增加计数器或记录指标值来捕获。对于那些情况,我们可以实现PublicMetrics接口,提供自己需要的度量信息。该接口定义了一个metrics()方法,返回一个Metric对象的集合:为了解PublicMetrics的使用方法,这里假设我们想报告一些源自Spring应用程序上下文的度量值——应用程序上下文启动的时间、 Bean及Bean定义的数量,这些都包含进来会很有意思。顺便再报告一下添加了@Controller注解的Bean的数量 。
@Component
public class ApplicationContextMetrics implements PublicMetrics {
private ApplicationContext context;
@Autowired
public ApplicationContextMetrics(ApplicationContext context) {
this.context = context;
}
@Override
public Collection<Metric<?>> metrics() {
List<Metric<?>> metrics = new ArrayList<Metric<?>>();
metrics.add(new Metric<Long>("spring.context.startup-date",
context.getStartupDate()));
metrics.add(new Metric<Integer>("spring.beans.definitions",
context.getBeanDefinitionCount()));
metrics.add(new Metric<Integer>("spring.beans",
context.getBeanNamesForType(Object.class).length));
metrics.add(new Metric<Integer>("spring.controllers",
context.getBeanNamesForAnnotation(Controller.class).length));
return metrics;
}
}
.1.4.创建自定义跟踪仓库;
默认情况下, /trace端点报告的跟踪信息都存储在内存仓库里, 100个条目封顶。一旦仓库满了,就开始移除老的条目,给新的条目腾出空间。在开发阶段这没什么问题,但在生产环境中,大流量会造成跟踪信息还没来得及看就被丢弃
@Service
public class MongoTraceRepository implements TraceRepository {
private MongoOperations mongoOps;
@Autowired
public MongoTraceRepository(MongoOperations mongoOps) {
this.mongoOps = mongoOps;
}
@Override
public List<Trace> findAll() {
return mongoOps.findAll(Trace.class);
}
@Override
public void add(Map<String, Object> traceInfo) {
mongoOps.save(new Trace(new Date(), traceInfo));
}
}
如果你用的是Maven,则需要如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
.1.5.插入自定义健康指示器;
Actuator自带了很多健康指示器,能满足常见需求,比如报告应用程序使用的数据库和消息代理的健康情况。但如果你的应用程序需要和一些没有健康指示器的系统交互,我们的阅读列表里有指向Amazon的图书链接,可以报告一下Amazon是否可以访问。当然,Amazon不太可能宕机,但不怕一万就怕万一,所以让我们为Amazon创建一个健康指示器吧。
@Component
public class AmazonHealth implements HealthIndicator {
@Override
public Health health() {
try {
RestTemplate rest = new RestTemplate();
rest.getForObject("http://www.amazon.com", String.class);
return Health.up().build();
} catch (Exception e) {
return Health.down().build();
}
}
}
AmazonHealth类并没有什么花哨的地方。 health()方法只是使用Spring的RestTemplate,向Amazon首页发起了一个GET请求。如果请求成功,则返回一个表明Amazon状态为UP的Health对象。如果请求发生异常,则health()返回一个标明Amazon状态为DOWN的Health对象,除了简单的状态之外,如果你还想向健康记录里添加其他附加信息,可以调用Health构造器的withDetail()方法。例如,要添加异常消息,将其作为健康记录的reason字段,可以让catch块返回这样一个Health对象:
return Health.down().withDetail("reason", e.getMessage()).build();