用过spring boot的都应该知道里面有一个actuator的starter,这个actuator其实非常有用,它提供了很多监控的endpoint,比如今天要讲的health,info。。。。最近在弄的spring boot admin2.0其实也是以这个jar为基础来做的
今天我们来讲一下health的endpoint。首先说一下spring boot 1.x和spring boot 2.x是有一些不一样的地方的,首先2.x版本的endpoint的地址也有有变化的,在1.x版本链接的基础上加“actuator”,以及想eureka jar的名称也不一样,配置也有些地方不一样,这些都是要注意的,如果你要从spring boot 1.x切换2.x
现在回归主题,来解剖一下这个health的endpoint
首先看一下入口HealthEndpoint这个类
标记1
这是actuator中提供大家实现自定义endpoint的注解,@endpoint也就是类似于@Controller,@ReadOperation, @WriteOperation, @DeleteOperation注解,分别对应生成Get/Post/Delete的Mapping
标记2
项目启动初始化HealthEndpoint中,属性healthIndicator被赋予CompositeHealthIndicator类,当我们访问health时,就会执行CompositeHealthIndicator的health方法
接下来看一下CompositeHealthIndicator类的health方法(看代码的注解)
#根据Spring boot的自动装配,根据相应的条件有没有满足,去加载各种的Indicator
public void addHealthIndicator(String name, HealthIndicator indicator) {
this.indicators.put(name, indicator);
}
#循环调用之前收集到的Indicator中的health方法,将结果集放在
#一个map里面
public Health health() {
Map<String, Health> healths = new LinkedHashMap();
Iterator var2 = this.indicators.entrySet().iterator();
while(var2.hasNext()) {
Entry<String, HealthIndicator> entry = (Entry)var2.next();
healths.put(entry.getKey(), ((HealthIndicator)entry.getValue()).health());
}
#执行healthAggregator的聚合方法,将上一步拿到的结果集map聚合到一个总的结果
return this.healthAggregator.aggregate(healths);
}
HealthIndicator的类结构大致如下(简单粗滤的画了一下,还有很多其他相关类没有画出)
然后再看看这个aggregate(聚合)方法代码,先附上关于healthAggregator的uml
先看AbstractHealthAggregator源码(讲解看注释):
public final Health aggregate(Map<String, Health> healths) {
#通过list.stream将status收集到一个list里面
List<Status> statusCandidates = (List)healths.values().stream().map(Health::getStatus).collect(Collectors.toList());
#将上一步拿到的list通过下面方法得到一个总的status
Status status = this.aggregateStatus(statusCandidates);
Map<String, Object> details = this.aggregateDetails(healths);
return (new Builder(status, details)).build();
}
protected abstract Status aggregateStatus(List<Status> var1);
protected Map<String, Object> aggregateDetails(Map<String, Health> healths) {
return new LinkedHashMap(healths);
}
再看this.aggregateStatus方法,AbstractHealthAggregator类中是抽象方法,没有具体的实现,所以要看具体的类OrderHealthAggregator
public OrderedHealthAggregator() {
this.setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN);
}
public void setStatusOrder(Status... statusOrder) {
String[] order = new String[statusOrder.length];
for(int i = 0; i < statusOrder.length; ++i) {
order[i] = statusOrder[i].getCode();
}
this.setStatusOrder(Arrays.asList(order));
}
public void setStatusOrder(List<String> statusOrder) {
Assert.notNull(statusOrder, "StatusOrder must not be null");
this.statusOrder = statusOrder;
}
protected Status aggregateStatus(List<Status> candidates) {
List<Status> filteredCandidates = new ArrayList();
Iterator var3 = candidates.iterator();
#将传进来的list中的元素,跟statusorder比较是否是范围之内的元素,不是就剔除
while(var3.hasNext()) {
Status candidate = (Status)var3.next();
if (this.statusOrder.contains(candidate.getCode())) {
filteredCandidates.add(candidate);
}
}
if (filteredCandidates.isEmpty()) {
return Status.UNKNOWN;
} else {
#将最终的list按照statusOrder的顺序排序,并将第一个取出
#排序的规则其实也是很简单,就是有down,就先取down,有OUT_OF_SERVICE就取OUT_OF_SERVICE
filteredCandidates.sort(new OrderedHealthAggregator.StatusComparator(this.statusOrder));
return (Status)filteredCandidates.get(0);
}
}
#排序的方法,实现Comparator,实现排序
private class StatusComparator implements Comparator<Status> {
private final List<String> statusOrder;
StatusComparator(List<String> var1) {
this.statusOrder = statusOrder;
}
#按照statusOrder的顺序进行比较
public int compare(Status s1, Status s2) {
int i1 = this.statusOrder.indexOf(s1.getCode());
int i2 = this.statusOrder.indexOf(s2.getCode());
return i1 < i2 ? -1 : (i1 != i2 ? 1 : s1.getCode().compareTo(s2.getCode()));
}
}
最后最关键的点来了,有没有人会问,什么时候会去调用CompositeHealthIndicator的addHealthIndicator方法
关键看HealthEndpointAutoConfiguration 类
@Configuration
@EnableConfigurationProperties({HealthEndpointProperties.class, HealthIndicatorProperties.class})
@AutoConfigureAfter({HealthIndicatorAutoConfiguration.class})
@Import({HealthEndpointConfiguration.class, HealthEndpointWebExtensionConfiguration.class})
public class HealthEndpointAutoConfiguration {
public HealthEndpointAutoConfiguration() {
}
}
然后是HealthEndpointConfiguration类
@Configuration
class HealthEndpointConfiguration {
HealthEndpointConfiguration() {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public HealthEndpoint healthEndpoint(ApplicationContext applicationContext) {
return new HealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext));
}
}
最后是HealthIndicatorBeansComposite类get方法
public static HealthIndicator get(ApplicationContext applicationContext) {
HealthAggregator healthAggregator = getHealthAggregator(applicationContext);
Map<String, HealthIndicator> indicators = new LinkedHashMap();
#利用反射机制获取实现HealthIndicator类(以加载在容器的上下文中)
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
if (ClassUtils.isPresent("reactor.core.publisher.Flux", (ClassLoader)null)) {
(new HealthIndicatorBeansComposite.ReactiveHealthIndicators()).get(applicationContext).forEach(indicators::putIfAbsent);
}
CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
return factory.createHealthIndicator(healthAggregator, indicators);
}
到这里我们清楚了,原来是CompositeHealthIndicatorFactory生产了一个 CompositeHealthIndicator以及入参是容器中的各种HealthIndicator类,到这里应该算是差不多了,如果有什么不对的地方大家可以提出来。这里虽然只介绍了health的,但可以举一反三,其它的endpoint也都差不多的
其实这种源码看多了有很多东西是差不多写发的,有没有,哈哈