本文主要研究一下EurekaHealthCheckHandlerhtml
eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckHandler.javajava
/** * This provides a more granular healthcheck contract than the existing {@link HealthCheckCallback} * * @author Nitesh Kant */ public interface HealthCheckHandler { InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus); }
netflix的eureka-client提供了HealthCheckHandler接口,用來對獲取服務實例的健康狀態
eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckCallbackToHandlerBridge.javaspring
@SuppressWarnings("deprecation") public class HealthCheckCallbackToHandlerBridge implements HealthCheckHandler { private final HealthCheckCallback callback; public HealthCheckCallbackToHandlerBridge() { callback = null; } public HealthCheckCallbackToHandlerBridge(HealthCheckCallback callback) { this.callback = callback; } @Override public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) { if (null == callback || InstanceInfo.InstanceStatus.STARTING == currentStatus || InstanceInfo.InstanceStatus.OUT_OF_SERVICE == currentStatus) { // Do not go to healthcheck handler if the status is starting or OOS. return currentStatus; } return callback.isHealthy() ? InstanceInfo.InstanceStatus.UP : InstanceInfo.InstanceStatus.DOWN; } }
這個類被標記爲廢棄,若是沒有callback,或者當前狀態是STARTING或OUT_OF_SERVICE,都會返回當前狀態;不然纔會調用callback的isHealthy方法來判斷是UP仍是DOWN.也就是說若是沒有callback,一開始啓動的時候是UP,則以後都是返回UP
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.javaspringboot
/** * Register {@link HealthCheckCallback} with the eureka client. * * Once registered, the eureka client will invoke the * {@link HealthCheckCallback} in intervals specified by * {@link EurekaClientConfig#getInstanceInfoReplicationIntervalSeconds()}. * * @param callback app specific healthcheck. * * @deprecated Use */ @Deprecated @Override public void registerHealthCheckCallback(HealthCheckCallback callback) { if (instanceInfo == null) { logger.error("Cannot register a listener for instance info since it is null!"); } if (callback != null) { healthCheckHandler = new HealthCheckCallbackToHandlerBridge(callback); } } @Override public void registerHealthCheck(HealthCheckHandler healthCheckHandler) { if (instanceInfo == null) { logger.error("Cannot register a healthcheck handler when instance info is null!"); } if (healthCheckHandler != null) { this.healthCheckHandler = healthCheckHandler; // schedule an onDemand update of the instanceInfo when a new healthcheck handler is registered if (instanceInfoReplicator != null) { instanceInfoReplicator.onDemandUpdate(); } } }
registerHealthCheckCallback被標記爲廢棄,緣由是它默認註冊了一個HealthCheckCallbackToHandlerBridge;後續是在這個方法裏頭處理fallback邏輯
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.javaapp
@Override public HealthCheckHandler getHealthCheckHandler() { if (healthCheckHandler == null) { if (null != healthCheckHandlerProvider) { healthCheckHandler = healthCheckHandlerProvider.get(); } else if (null != healthCheckCallbackProvider) { healthCheckHandler = new HealthCheckCallbackToHandlerBridge(healthCheckCallbackProvider.get()); } if (null == healthCheckHandler) { healthCheckHandler = new HealthCheckCallbackToHandlerBridge(null); } } return healthCheckHandler; }
這裏判斷,若是最後healthCheckHandler爲null,則會建立HealthCheckCallbackToHandlerBridge
eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckCallback.javaide
@Deprecated public interface HealthCheckCallback { /** * If false, the instance will be marked * {@link InstanceInfo.InstanceStatus#DOWN} with eureka. If the instance was * already marked {@link InstanceInfo.InstanceStatus#DOWN} , returning true * here will mark the instance back to * {@link InstanceInfo.InstanceStatus#UP}. * * @return true if the call back returns healthy, false otherwise. */ boolean isHealthy(); }
HealthCheckCallback目前被標記爲廢棄,能夠理解爲最後的healthCheckHandler = new HealthCheckCallbackToHandlerBridge(null);
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaServiceRegistry.javaui
@Override public void register(EurekaRegistration reg) { maybeInitializeClient(reg); if (log.isInfoEnabled()) { log.info("Registering application " + reg.getInstanceConfig().getAppname() + " with eureka with status " + reg.getInstanceConfig().getInitialStatus()); } reg.getApplicationInfoManager() .setInstanceStatus(reg.getInstanceConfig().getInitialStatus()); reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg.getEurekaClient().registerHealthCheck(healthCheckHandler)); }
這個方法判斷,若是有healthCheckHandler實例,則纔會調用registerHealthCheck去註冊。
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.javathis
@Configuration @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) public class EurekaDiscoveryClientConfiguration { class Marker {} @Bean public Marker eurekaDiscoverClientMarker() { return new Marker(); } @Configuration @ConditionalOnClass(RefreshScopeRefreshedEvent.class) protected static class EurekaClientConfigurationRefresher { @Autowired(required = false) private EurekaClient eurekaClient; @Autowired(required = false) private EurekaAutoServiceRegistration autoRegistration; @EventListener(RefreshScopeRefreshedEvent.class) public void onApplicationEvent(RefreshScopeRefreshedEvent event) { //This will force the creation of the EurkaClient bean if not already created //to make sure the client will be reregistered after a refresh event if(eurekaClient != null) { eurekaClient.getApplications(); } if (autoRegistration != null) { // register in case meta data changed this.autoRegistration.stop(); this.autoRegistration.start(); } } } @Configuration @ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false) protected static class EurekaHealthCheckHandlerConfiguration { @Autowired(required = false) private HealthAggregator healthAggregator = new OrderedHealthAggregator(); @Bean @ConditionalOnMissingBean(HealthCheckHandler.class) public EurekaHealthCheckHandler eurekaHealthCheckHandler() { return new EurekaHealthCheckHandler(this.healthAggregator); } } }
默認eureka.client.healthcheck.enabled爲false,若是設置爲true的話則會注入EurekaHealthCheckHandler
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.javarest
public class EurekaHealthCheckHandler implements HealthCheckHandler, ApplicationContextAware, InitializingBean { private static final Map<Status, InstanceInfo.InstanceStatus> STATUS_MAPPING = new HashMap<Status, InstanceInfo.InstanceStatus>() {{ put(Status.UNKNOWN, InstanceStatus.UNKNOWN); put(Status.OUT_OF_SERVICE, InstanceStatus.OUT_OF_SERVICE); put(Status.DOWN, InstanceStatus.DOWN); put(Status.UP, InstanceStatus.UP); }}; private final CompositeHealthIndicator healthIndicator; private ApplicationContext applicationContext; public EurekaHealthCheckHandler(HealthAggregator healthAggregator) { Assert.notNull(healthAggregator, "HealthAggregator must not be null"); this.healthIndicator = new CompositeHealthIndicator(healthAggregator); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() throws Exception { final Map<String, HealthIndicator> healthIndicators = applicationContext.getBeansOfType(HealthIndicator.class); for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) { //ignore EurekaHealthIndicator and flatten the rest of the composite //otherwise there is a never ending cycle of down. See gh-643 if (entry.getValue() instanceof DiscoveryCompositeHealthIndicator) { DiscoveryCompositeHealthIndicator indicator = (DiscoveryCompositeHealthIndicator) entry.getValue(); for (DiscoveryCompositeHealthIndicator.Holder holder : indicator.getHealthIndicators()) { if (!(holder.getDelegate() instanceof EurekaHealthIndicator)) { healthIndicator.addHealthIndicator(holder.getDelegate().getName(), holder); } } } else { healthIndicator.addHealthIndicator(entry.getKey(), entry.getValue()); } } } @Override public InstanceStatus getStatus(InstanceStatus instanceStatus) { return getHealthStatus(); } protected InstanceStatus getHealthStatus() { final Status status = getHealthIndicator().health().getStatus(); return mapToInstanceStatus(status); } protected InstanceStatus mapToInstanceStatus(Status status) { if (!STATUS_MAPPING.containsKey(status)) { return InstanceStatus.UNKNOWN; } return STATUS_MAPPING.get(status); } protected CompositeHealthIndicator getHealthIndicator() { return healthIndicator; } }
能夠看到在afterPropertiesSet的時候,將整個springboot的healthIndicator添加進來並轉化映射爲eureka的InstanceStatus,組合爲CompositeHealthIndicator。而client的health check則會調用這個getStatus接口,返回的是compositeHealthIndicator的健康狀態。
eureka client的health check默認是false,即最後使用的是HealthCheckCallbackToHandlerBridge,即HealthCheckCallbackToHandlerBridge(null),callback爲null,則默認返回的都是啓動時註冊的狀態,通常是UP。若是開啓eureka.client.healthcheck.enabled=true則會將springboot的actuator的health indicator都歸入health check當中。code