From 0b8f85529c8f7bc0463d62f4ce8cf4786e4a3ab5 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 25 Feb 2015 09:16:11 +0000 Subject: [PATCH] Refresh health indicator for config server if present Listen for heartbeats from discovery so that state changes can trigger a new status (and the client doesn't rely on the old address of the server if it has now changed). Fixes gh-232 --- ...ntConfigServiceBootstrapConfiguration.java | 45 ++++++++++++++----- .../ribbon/RibbonClientConfiguration.java | 22 +++++---- .../netflix/zuul/ZuulProxyConfiguration.java | 9 ++-- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfiguration.java index 9b96cae1..f60e3be0 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfiguration.java @@ -22,15 +22,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.cloud.client.discovery.event.HeartbeatMonitor; import org.springframework.cloud.config.client.ConfigClientProperties; import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator; import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; -import org.springframework.context.ApplicationListener; +import org.springframework.context.ApplicationEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; +import org.springframework.context.event.SmartApplicationListener; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.DiscoveryClient; @@ -48,7 +49,9 @@ import com.netflix.discovery.DiscoveryClient; @Import(EurekaClientAutoConfiguration.class) @CommonsLog public class DiscoveryClientConfigServiceBootstrapConfiguration implements - ApplicationListener { + SmartApplicationListener { + + private HeartbeatMonitor monitor = new HeartbeatMonitor(); @Autowired private DiscoveryClient client; @@ -57,14 +60,36 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration implements private ConfigClientProperties config; @Override - public void onApplicationEvent(ContextRefreshedEvent event) { + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ContextRefreshedEvent) { + refresh(); + } + else if (event instanceof HeartbeatEvent) { + if (this.monitor.update(((HeartbeatEvent) event).getValue())) { + refresh(); + } + } + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public boolean supportsEventType(Class eventType) { + return ContextRefreshedEvent.class.isAssignableFrom(eventType) + || HeartbeatEvent.class.isAssignableFrom(eventType); + } + + @Override + public boolean supportsSourceType(Class sourceType) { + return true; + } + + private void refresh() { try { log.info("Locating configserver via discovery"); - Environment environment = event.getApplicationContext().getEnvironment(); - if (!(environment instanceof ConfigurableEnvironment)) { - log.info("Environment is not ConfigurableEnvironment so cannot look up configserver"); - return; - } InstanceInfo server = this.client.getNextServerFromEureka(this.config .getDiscovery().getServiceId(), false); String url = server.getHomePageUrl(); diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java index 37101b99..58d507ff 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java @@ -24,7 +24,17 @@ import org.springframework.context.annotation.Configuration; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.*; +import com.netflix.loadbalancer.ConfigurationBasedServerList; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.IPing; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.LoadBalancerBuilder; +import com.netflix.loadbalancer.NoOpPing; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.netflix.loadbalancer.ServerListFilter; +import com.netflix.loadbalancer.ZoneAvoidanceRule; +import com.netflix.loadbalancer.ZoneAwareLoadBalancer; import com.netflix.niws.client.http.RestClient; import com.netflix.servo.monitor.Monitors; @@ -85,15 +95,11 @@ public class RibbonClientConfiguration { @Bean @ConditionalOnMissingBean public ILoadBalancer ribbonLoadBalancer(IClientConfig config, - ServerList serverList, - ServerListFilter serverListFilter, + ServerList serverList, ServerListFilter serverListFilter, IRule rule, IPing ping) { ZoneAwareLoadBalancer balancer = LoadBalancerBuilder.newBuilder() - .withClientConfig(config) - .withRule(rule) - .withPing(ping) - .withServerListFilter(serverListFilter) - .withDynamicServerList(serverList) + .withClientConfig(config).withRule(rule).withPing(ping) + .withServerListFilter(serverListFilter).withDynamicServerList(serverList) .buildDynamicServerListLoadBalancer(); return balancer; } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java index c6dc481a..2c7a2bd4 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.cloud.netflix.zuul; -import java.util.concurrent.atomic.AtomicReference; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.trace.TraceRepository; @@ -25,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.cloud.client.discovery.event.HeartbeatMonitor; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; @@ -121,7 +120,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { private static class ZuulRefreshListener implements ApplicationListener { - private AtomicReference latestHeartbeat = new AtomicReference<>(); + private HeartbeatMonitor monitor = new HeartbeatMonitor(); @Autowired private ProxyRouteLocator routeLocator; @@ -148,9 +147,7 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { } private void resetIfNeeded(Object value) { - if (this.latestHeartbeat.get() == null - || !this.latestHeartbeat.get().equals(value)) { - this.latestHeartbeat.set(value); + if (this.monitor.update(value)) { reset(); } }