1. 程式人生 > >actuator 中health這個endpoint的原始碼解剖

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也都差不多的

    其實這種原始碼看多了有很多東西是差不多寫發的,有沒有,哈哈