Use custom DataCenterInfo to make instance ID unique
The problem manifests itself as errors in the Eureka server
log (gh-47), but originates in the client because it is sending
requests to /eureka/apps/{NAME}/{ID} with the wrong ID. The
InstanceInfo has an ID that is derived (for preference) from
the EurekaInstanceConfig.dataCenterInfo, so that's the best
way to fix it.
See gh-63, Fixes gh-47
This commit is contained in:
@@ -24,6 +24,7 @@ import com.thoughtworks.xstream.MarshallingStrategy;
|
||||
import com.thoughtworks.xstream.converters.Converter;
|
||||
import com.thoughtworks.xstream.converters.ConverterLookup;
|
||||
import com.thoughtworks.xstream.converters.DataHolder;
|
||||
import com.thoughtworks.xstream.converters.MarshallingContext;
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import com.thoughtworks.xstream.core.TreeMarshallingStrategy;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
|
||||
@@ -57,7 +58,8 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
@Override
|
||||
public void marshal(HierarchicalStreamWriter writer, Object obj,
|
||||
ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) {
|
||||
delegate.marshal(writer, obj, converterLookup, mapper, dataHolder);
|
||||
ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup);
|
||||
delegate.marshal(writer, obj, wrapped, mapper, dataHolder);
|
||||
}
|
||||
|
||||
public static class InstanceIdDataCenterInfo implements DataCenterInfo,
|
||||
@@ -101,6 +103,22 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
}
|
||||
|
||||
private static class DataCenterAwareConverter extends InstanceInfoConverter {
|
||||
|
||||
@Override
|
||||
public void marshal(Object source, HierarchicalStreamWriter writer,
|
||||
MarshallingContext context) {
|
||||
InstanceInfo info = (InstanceInfo) source;
|
||||
String instanceId = info.getMetadata().get("instanceId");
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
source = info;
|
||||
}
|
||||
super.marshal(source, writer, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
@@ -111,8 +129,9 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
|
||||
DataCenterInfo dataCenter = info.getDataCenterInfo();
|
||||
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
|
||||
String old = info.getId();
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
info = new InstanceInfo.Builder(info).setDataCenterInfo(
|
||||
new InstanceIdDataCenterInfo(old + ":" + instanceId)).build();
|
||||
new InstanceIdDataCenterInfo(id)).build();
|
||||
obj = info;
|
||||
}
|
||||
return obj;
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import com.netflix.appinfo.DataCenterInfo;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -76,10 +77,7 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
|
||||
|
||||
private Map<String, String> metadataMap = new HashMap<>();
|
||||
|
||||
private DataCenterInfo dataCenterInfo = new DataCenterInfo() {
|
||||
@Getter @Setter
|
||||
private Name name = Name.MyOwn;
|
||||
};
|
||||
private DataCenterInfo dataCenterInfo = new IdentifyingDataCenterInfo();
|
||||
|
||||
private String ipAddress = hostInfo[0];
|
||||
|
||||
@@ -130,4 +128,21 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
|
||||
return preferIpAddress ? ipAddress : hostname;
|
||||
}
|
||||
|
||||
private final class IdentifyingDataCenterInfo implements DataCenterInfo, UniqueIdentifier {
|
||||
@Getter @Setter
|
||||
private Name name = Name.MyOwn;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
String instanceId = metadataMap.get("instanceId");
|
||||
if (instanceId != null) {
|
||||
String old = hostname;
|
||||
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
|
||||
return id;
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,10 +22,12 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.boot.test.EnvironmentTestUtils.*;
|
||||
|
||||
/**
|
||||
@@ -43,103 +45,102 @@ public class EurekaInstanceConfigBeanTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void idFromInstanceId() throws Exception {
|
||||
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean();
|
||||
instance.getMetadataMap().put("instanceId", "foo");
|
||||
instance.setHostname("bar");
|
||||
assertEquals("bar:foo", ((UniqueIdentifier) instance.getDataCenterInfo()).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicBinding() {
|
||||
addEnvironment(context, "eureka.instance.appGroupName=mygroup");
|
||||
setupContext();
|
||||
setupContext();
|
||||
assertEquals("mygroup", getInstanceConfig().getAppGroupName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecurePort() {
|
||||
testNonSecurePort("eureka.instance.nonSecurePort");
|
||||
}
|
||||
testNonSecurePort("eureka.instance.nonSecurePort");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecurePort2() {
|
||||
testNonSecurePort("server.port");
|
||||
}
|
||||
@Test
|
||||
public void nonSecurePort2() {
|
||||
testNonSecurePort("server.port");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecurePort3() {
|
||||
testNonSecurePort("SERVER_PORT");
|
||||
}
|
||||
@Test
|
||||
public void nonSecurePort3() {
|
||||
testNonSecurePort("SERVER_PORT");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSecurePort4() {
|
||||
testNonSecurePort("PORT");
|
||||
}
|
||||
@Test
|
||||
public void nonSecurePort4() {
|
||||
testNonSecurePort("PORT");
|
||||
}
|
||||
|
||||
private void testNonSecurePort(String propName) {
|
||||
addEnvironment(context, propName + ":8888");
|
||||
setupContext();
|
||||
assertEquals(8888, getInstanceConfig().getNonSecurePort());
|
||||
}
|
||||
private void testNonSecurePort(String propName) {
|
||||
addEnvironment(context, propName + ":8888");
|
||||
setupContext();
|
||||
assertEquals(8888, getInstanceConfig().getNonSecurePort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInitialStatus() {
|
||||
setupContext();
|
||||
assertEquals("initialStatus wrong", InstanceStatus.UP,
|
||||
getInstanceConfig().getInitialStatus());
|
||||
}
|
||||
@Test
|
||||
public void testDefaultInitialStatus() {
|
||||
setupContext();
|
||||
assertEquals("initialStatus wrong", InstanceStatus.UP, getInstanceConfig()
|
||||
.getInitialStatus());
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadInitialStatus() {
|
||||
addEnvironment(context, "eureka.instance.initial-status:FOO");
|
||||
setupContext();
|
||||
}
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void testBadInitialStatus() {
|
||||
addEnvironment(context, "eureka.instance.initial-status:FOO");
|
||||
setupContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomInitialStatus() {
|
||||
addEnvironment(context, "eureka.instance.initial-status:STARTING");
|
||||
setupContext();
|
||||
assertEquals("initialStatus wrong", InstanceStatus.STARTING,
|
||||
getInstanceConfig().getInitialStatus());
|
||||
}
|
||||
@Test
|
||||
public void testCustomInitialStatus() {
|
||||
addEnvironment(context, "eureka.instance.initial-status:STARTING");
|
||||
setupContext();
|
||||
assertEquals("initialStatus wrong", InstanceStatus.STARTING, getInstanceConfig()
|
||||
.getInitialStatus());
|
||||
}
|
||||
|
||||
private void setupContext() {
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
TestConfiguration.class);
|
||||
context.refresh();
|
||||
}
|
||||
private void setupContext() {
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
TestConfiguration.class);
|
||||
context.refresh();
|
||||
}
|
||||
|
||||
protected EurekaInstanceConfigBean getInstanceConfig() {
|
||||
return context.getBean(EurekaInstanceConfigBean.class);
|
||||
}
|
||||
protected EurekaInstanceConfigBean getInstanceConfig() {
|
||||
return context.getBean(EurekaInstanceConfigBean.class);
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void serviceUrlWithCompositePropertySource() {
|
||||
CompositePropertySource source = new CompositePropertySource("composite");
|
||||
context.getEnvironment().getPropertySources().addFirst(source);
|
||||
source.addPropertySource(new MapPropertySource("config", Collections
|
||||
.<String, Object> singletonMap("eureka.client.serviceUrl.defaultZone",
|
||||
"http://example.com")));
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
TestConfiguration.class);
|
||||
context.refresh();
|
||||
assertEquals("{defaultZone=http://example.com}",
|
||||
context.getBean(EurekaInstanceConfigBean.class).getServiceUrl().toString());
|
||||
assertEquals(
|
||||
"[http://example.com]",
|
||||
context.getBean(EurekaInstanceConfigBean.class)
|
||||
.getEurekaServerServiceUrls("defaultZone").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serviceUrlWithDefault() {
|
||||
EnvironmentTestUtils.addEnvironment(context,
|
||||
"eureka.client.serviceUrl.defaultZone:",
|
||||
"eureka.client.serviceUrl.default:http://example.com");
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
TestConfiguration.class);
|
||||
context.refresh();
|
||||
assertEquals(
|
||||
"[http://example.com]",
|
||||
context.getBean(EurekaInstanceConfigBean.class)
|
||||
.getEurekaServerServiceUrls("defaultZone").toString());
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* @Test public void serviceUrlWithCompositePropertySource() { CompositePropertySource
|
||||
* source = new CompositePropertySource("composite");
|
||||
* context.getEnvironment().getPropertySources().addFirst(source);
|
||||
* source.addPropertySource(new MapPropertySource("config", Collections .<String,
|
||||
* Object> singletonMap("eureka.client.serviceUrl.defaultZone",
|
||||
* "http://example.com")));
|
||||
* context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
* TestConfiguration.class); context.refresh();
|
||||
* assertEquals("{defaultZone=http://example.com}",
|
||||
* context.getBean(EurekaInstanceConfigBean.class).getServiceUrl().toString());
|
||||
* assertEquals( "[http://example.com]",
|
||||
* context.getBean(EurekaInstanceConfigBean.class)
|
||||
* .getEurekaServerServiceUrls("defaultZone").toString()); }
|
||||
*
|
||||
* @Test public void serviceUrlWithDefault() {
|
||||
* EnvironmentTestUtils.addEnvironment(context,
|
||||
* "eureka.client.serviceUrl.defaultZone:",
|
||||
* "eureka.client.serviceUrl.default:http://example.com");
|
||||
* context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
* TestConfiguration.class); context.refresh(); assertEquals( "[http://example.com]",
|
||||
* context.getBean(EurekaInstanceConfigBean.class)
|
||||
* .getEurekaServerServiceUrls("defaultZone").toString()); }
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(EurekaInstanceConfigBean.class)
|
||||
protected static class TestConfiguration {
|
||||
|
||||
Reference in New Issue
Block a user