From ea4ea4a54d591ff4917ac815d8a841c72dfde3a8 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Thu, 22 Jan 2015 16:08:05 -0700 Subject: [PATCH] proxy DiscoveryHeartbeatEvent's from parent to child be publishing a EurekaHeartbeatEvent and having zuul listen for EurekaHeartbeatEvent as well. fixes gh-171 --- .../eureka/EurekaClientAutoConfiguration.java | 42 +++++++++++++++++-- .../EurekaDiscoveryClientConfiguration.java | 13 +++--- .../netflix/eureka/EurekaHeartbeatEvent.java | 22 ++++++++++ .../netflix/zuul/ZuulProxyConfiguration.java | 19 ++++++--- 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHeartbeatEvent.java diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java index a33089a7b..2993207f2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java @@ -22,8 +22,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.builder.ParentContextApplicationContextInitializer; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.DiscoveryHeartbeatEvent; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,6 +36,9 @@ import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.converters.JsonXStream; import com.netflix.discovery.converters.XmlXStream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * @author Dave Syer */ @@ -39,17 +46,19 @@ import com.netflix.discovery.converters.XmlXStream; @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) -public class EurekaClientAutoConfiguration { +public class EurekaClientAutoConfiguration implements ApplicationListener { @Autowired - private ApplicationContext context; + private ApplicationContext applicationContext; + + private static final ConcurrentMap listenerAdded = new ConcurrentHashMap<>(); @PostConstruct public void init() { XmlXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy(this.context)); + new DataCenterAwareMarshallingStrategy(this.applicationContext)); JsonXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy(this.context)); + new DataCenterAwareMarshallingStrategy(this.applicationContext)); } @Bean @@ -64,4 +73,29 @@ public class EurekaClientAutoConfiguration { return new EurekaInstanceConfigBean(); } + + /** + * propagate DiscoveryHeartbeatEvent from parent to child. + * Do it via a EurekaHeartbeatEvent since events get published to the + * parent context, otherwise results in a stack overflow + * @param event + */ + @Override + public void onApplicationEvent(final ParentContextApplicationContextInitializer.ParentContextAvailableEvent event) { + final ConfigurableApplicationContext context = event.getApplicationContext(); + String childId = context.getId(); + ApplicationContext parent = context.getParent(); + if (parent != null && "bootstrap".equals(parent.getId()) + && parent instanceof ConfigurableApplicationContext) { + if (listenerAdded.putIfAbsent(childId, childId) == null) { + ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) parent; + ctx.addApplicationListener(new ApplicationListener() { + @Override + public void onApplicationEvent(DiscoveryHeartbeatEvent dhe) { + context.publishEvent(new EurekaHeartbeatEvent(dhe)); + } + }); + } + } + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java index c5055614f..f1e935acb 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java @@ -94,11 +94,14 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order if (jerseyClientField != null) { try { jerseyClientField.setAccessible(true); - Object obj = jerseyClientField.get(DiscoveryManager.getInstance() - .getDiscoveryClient()); - if (obj != null) { - EurekaJerseyClient.JerseyClient jerseyClient = (EurekaJerseyClient.JerseyClient) obj; - jerseyClient.destroyResources(); + if (DiscoveryManager.getInstance() != null && + DiscoveryManager.getInstance().getDiscoveryClient() != null) { + Object obj = jerseyClientField.get(DiscoveryManager.getInstance() + .getDiscoveryClient()); + if (obj != null) { + EurekaJerseyClient.JerseyClient jerseyClient = (EurekaJerseyClient.JerseyClient) obj; + jerseyClient.destroyResources(); + } } } catch (Exception ex) { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHeartbeatEvent.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHeartbeatEvent.java new file mode 100644 index 000000000..0599396a8 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHeartbeatEvent.java @@ -0,0 +1,22 @@ +package org.springframework.cloud.netflix.eureka; + +import org.springframework.cloud.client.discovery.DiscoveryHeartbeatEvent; +import org.springframework.context.ApplicationEvent; + +/** + * Specifically used when eureka is in the parent bootstrap context to relay the DiscoveryHeartbeatEvent to the child. Avoids stack overflow + * @author Spencer Gibb + */ +public class EurekaHeartbeatEvent extends ApplicationEvent { + + private final Object value; + + public EurekaHeartbeatEvent(DiscoveryHeartbeatEvent e) { + super(e.getSource()); + value = e.getValue(); + } + + public Object getValue() { + return value; + } +} 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 ec5ce8523..8a59236fc 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 @@ -26,6 +26,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.DiscoveryHeartbeatEvent; import org.springframework.cloud.client.discovery.InstanceRegisteredEvent; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; +import org.springframework.cloud.netflix.eureka.EurekaHeartbeatEvent; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter; @@ -126,17 +127,25 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { || event instanceof RoutesRefreshedEvent) { reset(); } + else if (event instanceof EurekaHeartbeatEvent) { + EurekaHeartbeatEvent e = (EurekaHeartbeatEvent) event; + resetIfNeeded(e.getValue()); + } else if (event instanceof DiscoveryHeartbeatEvent) { DiscoveryHeartbeatEvent e = (DiscoveryHeartbeatEvent) event; - if (this.latestHeartbeat.get() == null - || !this.latestHeartbeat.get().equals(e.getValue())) { - this.latestHeartbeat.set(e.getValue()); - reset(); - } + resetIfNeeded(e.getValue()); } } + private void resetIfNeeded(Object value) { + if (this.latestHeartbeat.get() == null + || !this.latestHeartbeat.get().equals(value)) { + this.latestHeartbeat.set(value); + reset(); + } + } + private void reset() { this.routeLocator.resetRoutes(); this.zuulHandlerMapping.registerHandlers();