actuator 中health這個endpoint的原始碼解剖
用過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也都差不多的
其實這種原始碼看多了有很多東西是差不多寫發的,有沒有,哈哈