proxy DiscoveryHeartbeatEvent's from parent to child be publishing a EurekaHeartbeatEvent and having zuul listen for EurekaHeartbeatEvent as well.
fixes gh-171
This commit is contained in:
@@ -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<ParentContextApplicationContextInitializer.ParentContextAvailableEvent> {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private static final ConcurrentMap<String, String> 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<DiscoveryHeartbeatEvent>() {
|
||||
@Override
|
||||
public void onApplicationEvent(DiscoveryHeartbeatEvent dhe) {
|
||||
context.publishEvent(new EurekaHeartbeatEvent(dhe));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user