本文主要研究一下EurekaHealthCheckHandler

HealthCheckHandler

eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckHandler.java

/**
 * 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接口,用来对获取服务实例的健康状态

HealthCheckCallbackToHandlerBridge

eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckCallbackToHandlerBridge.java

@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

registerHealthCheck

eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.java

    /**
     * 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逻辑

getHealthCheckHandler

eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.java

    @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

HealthCheckCallback

eureka-client-1.8.8-sources.jar!/com/netflix/appinfo/HealthCheckCallback.java

@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);

EurekaServiceRegistry

spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaServiceRegistry.java

    @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去注册。

EurekaDiscoveryClientConfiguration

spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java

@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

EurekaHealthCheckHandler

spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaHealthCheckHandler.java

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当中。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...