diff --git a/pom.xml b/pom.xml index 88be448b..7ac1de3b 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 1.1.0.BUILD-SNAPSHOT ${basedir} 0.6.5 - 1.1.147 + 1.2.0 8.6.0 1.4.13 2.0.0 @@ -187,6 +187,11 @@ + + com.netflix.governator + governator + 1.3.3 + com.netflix.eureka eureka-core diff --git a/spring-cloud-netflix-core/pom.xml b/spring-cloud-netflix-core/pom.xml index a9d685ef..79f36e92 100644 --- a/spring-cloud-netflix-core/pom.xml +++ b/spring-cloud-netflix-core/pom.xml @@ -59,6 +59,12 @@ eureka-client true + + com.netflix.governator + governator + 1.3.3 + true + com.netflix.eureka eureka-core diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java index 362982bd..e8d98693 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.config; import javax.annotation.PostConstruct; +import com.netflix.discovery.EurekaClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -25,9 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationPropertiesBindin import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration; import org.springframework.context.annotation.Configuration; -import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; -import com.netflix.discovery.DiscoveryManager; import com.netflix.discovery.EurekaClientConfig; /** @@ -53,17 +52,18 @@ public class DiscoveryClientConfigServiceAutoConfiguration { @Autowired private EurekaDiscoveryClientConfiguration lifecycle; + @Autowired + private EurekaClient eurekaClient; + @PostConstruct public void init() { this.lifecycle.stop(); rebind(this.clientConfig, "eurekaClientConfig"); rebind(this.instanceConfig, "eurekaInstanceConfig"); - if (DiscoveryManager.getInstance().getDiscoveryClient() != null) { - DiscoveryManager.getInstance().getDiscoveryClient().shutdown(); - } - ApplicationInfoManager.getInstance().initComponent(this.instanceConfig); - DiscoveryManager.getInstance().initComponent(this.instanceConfig, - this.clientConfig); + eurekaClient.shutdown();; + // FIXME: reinit EurekaClient and ApplicationInfoManager + //applicationInfoManager.initComponent(this.instanceConfig); + //discoveryManager.initComponent(this.instanceConfig, this.clientConfig); this.lifecycle.start(); } 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 5f8a8b53..0d6ed4ab 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 @@ -34,8 +34,7 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SmartApplicationListener; import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.DiscoveryClient; -import com.netflix.discovery.DiscoveryManager; +import com.netflix.discovery.EurekaClient; /** * Bootstrap configuration for a config client that wants to lookup the config server via @@ -43,7 +42,7 @@ import com.netflix.discovery.DiscoveryManager; * * @author Dave Syer */ -@ConditionalOnClass({ DiscoveryClient.class, ConfigServicePropertySourceLocator.class }) +@ConditionalOnClass({ EurekaClient.class, ConfigServicePropertySourceLocator.class }) @ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false) @Configuration @EnableDiscoveryClient @@ -57,6 +56,9 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration implements @Autowired private ConfigClientProperties config; + @Autowired + private EurekaClient eurekaClient; + @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { @@ -88,9 +90,7 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration implements private void refresh() { try { log.info("Locating configserver via discovery"); - InstanceInfo server = DiscoveryManager - .getInstance() - .getDiscoveryClient() + InstanceInfo server = eurekaClient .getNextServerFromEureka(this.config.getDiscovery().getServiceId(), false); String url = server.getHomePageUrl(); diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/EurekaClientConfigServerAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/EurekaClientConfigServerAutoConfiguration.java index 5fe44d69..9fc31d6f 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/EurekaClientConfigServerAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/EurekaClientConfigServerAutoConfiguration.java @@ -26,7 +26,7 @@ import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; -import com.netflix.discovery.DiscoveryClient; +import com.netflix.discovery.EurekaClient; /** * Extra configuration for config server if it happens to be a Eureka instance. @@ -35,7 +35,7 @@ import com.netflix.discovery.DiscoveryClient; */ @Configuration @EnableConfigurationProperties -@ConditionalOnClass({ EurekaInstanceConfigBean.class, DiscoveryClient.class, +@ConditionalOnClass({ EurekaInstanceConfigBean.class, EurekaClient.class, ConfigServerProperties.class }) public class EurekaClientConfigServerAutoConfiguration { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java new file mode 100644 index 00000000..984c4c58 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.eureka; + +import java.util.concurrent.atomic.AtomicLong; + +import lombok.extern.apachecommons.CommonsLog; + +import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.context.ApplicationContext; + +import com.netflix.appinfo.ApplicationInfoManager; +import com.netflix.discovery.DiscoveryClient; +import com.netflix.discovery.EurekaClientConfig; + +/** + * @author Spencer Gibb + */ +@CommonsLog +public class CloudEurekaClient extends DiscoveryClient { + + private final AtomicLong cacheRefreshedCount = new AtomicLong(0); + + private ApplicationContext context; + + public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, + EurekaClientConfig config, ApplicationContext context) { + super(applicationInfoManager, config); + this.context = context; + } + + @Override + protected void onCacheRefreshed() { + if (this.cacheRefreshedCount != null) { //might be call during construction and won't be inited yet + long newCount = this.cacheRefreshedCount.incrementAndGet(); + log.trace("onCacheRefreshed called with count: " + newCount); + this.context.publishEvent(new HeartbeatEvent(this, newCount)); + } + } +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareJacksonCodec.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareJacksonCodec.java new file mode 100644 index 00000000..ea18e02d --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareJacksonCodec.java @@ -0,0 +1,144 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.eureka; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Map; + +import lombok.SneakyThrows; +import lombok.extern.apachecommons.CommonsLog; + +import org.springframework.util.ReflectionUtils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.netflix.appinfo.DataCenterInfo; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.LeaseInfo; +import com.netflix.discovery.converters.EurekaJacksonCodec; +import com.netflix.discovery.converters.StringCache; +import com.netflix.discovery.shared.Application; +import com.netflix.discovery.shared.Applications; + +/** + * @author Spencer Gibb + */ +public class DataCenterAwareJacksonCodec extends EurekaJacksonCodec { + private static final Version VERSION = new Version(1, 1, 0, null); + + @SneakyThrows + public DataCenterAwareJacksonCodec() { + super(); + + ObjectMapper mapper = new ObjectMapper(); + + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + SimpleModule module = new SimpleModule("eureka1.x", VERSION); + module.addSerializer(DataCenterInfo.class, new DataCenterInfoSerializer()); + module.addSerializer(InstanceInfo.class, new DCAwareInstanceInfoSerializer()); + module.addSerializer(Application.class, new ApplicationSerializer()); + module.addSerializer(Applications.class, new ApplicationsSerializer(getVersionDeltaKey(), getAppHashCodeKey())); + + module.addDeserializer(DataCenterInfo.class, new DataCenterInfoDeserializer(getCache())); + module.addDeserializer(LeaseInfo.class, new LeaseInfoDeserializer()); + module.addDeserializer(InstanceInfo.class, new DCAwareInstanceInfoDeserializer(getCache())); + module.addDeserializer(Application.class, new ApplicationDeserializer(mapper, getCache())); + module.addDeserializer(Applications.class, new ApplicationsDeserializer(mapper, getVersionDeltaKey(), getAppHashCodeKey())); + + mapper.registerModule(module); + + Map, ObjectReader> readers = getField("objectReaderByClass"); + readers.put(InstanceInfo.class, mapper.reader().withType(InstanceInfo.class).withRootName("instance")); + readers.put(Application.class, mapper.reader().withType(Application.class).withRootName("application")); + readers.put(Applications.class, mapper.reader().withType(Applications.class).withRootName("applications")); + + Map, ObjectWriter> writers = getField("objectWriterByClass"); + writers.put(InstanceInfo.class, mapper.writer().withType(InstanceInfo.class).withRootName("instance")); + writers.put(Application.class, mapper.writer().withType(Application.class).withRootName("application")); + writers.put(Applications.class, mapper.writer().withType(Applications.class).withRootName("applications")); + + Field field = ReflectionUtils.findField(EurekaJacksonCodec.class, "mapper"); + ReflectionUtils.makeAccessible(field); + field.set(this, mapper); + } + + private T getField(String name) throws IllegalAccessException { + Field field = ReflectionUtils.findField(EurekaJacksonCodec.class, name); + ReflectionUtils.makeAccessible(field); + return (T) field.get(this); + } + + @SneakyThrows + public static void init() { + if (!(EurekaJacksonCodec.getInstance() instanceof DataCenterAwareJacksonCodec)) { + INSTANCE = new DataCenterAwareJacksonCodec(); + } + } + + @CommonsLog + private static class DCAwareInstanceInfoSerializer extends + InstanceInfoSerializer { + + @Override + public void serialize(InstanceInfo info, JsonGenerator jgen, + SerializerProvider provider) throws IOException { + String instanceId = info.getMetadata().get("instanceId"); + DataCenterInfo dataCenter = info.getDataCenterInfo(); + if (instanceId != null + && DataCenterInfo.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(); + } + + super.serialize(info, jgen, provider); + } + } + + private class DCAwareInstanceInfoDeserializer extends InstanceInfoDeserializer { + private DCAwareInstanceInfoDeserializer(StringCache cache) { + super(getMapper(), cache); + } + + @Override + public InstanceInfo deserialize(JsonParser jp, DeserializationContext context) + throws IOException { + InstanceInfo info = super.deserialize(jp, context); + String instanceId = info.getMetadata().get("instanceId"); + DataCenterInfo dataCenter = info.getDataCenterInfo(); + if (instanceId != null && DataCenterInfo.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(); + } + return info; + } + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java index b6d36b37..49d67d6a 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java @@ -16,19 +16,12 @@ package org.springframework.cloud.netflix.eureka; -import lombok.extern.apachecommons.CommonsLog; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.cloud.client.discovery.event.HeartbeatEvent; -import org.springframework.context.ApplicationContext; - import com.netflix.appinfo.DataCenterInfo; import com.netflix.appinfo.DataCenterInfo.Name; import com.netflix.appinfo.InstanceInfo; -import com.netflix.appinfo.UniqueIdentifier; import com.netflix.discovery.converters.Converters.ApplicationsConverter; import com.netflix.discovery.converters.Converters.InstanceInfoConverter; +import com.netflix.discovery.converters.StringCache; import com.netflix.discovery.shared.Applications; import com.thoughtworks.xstream.MarshallingStrategy; import com.thoughtworks.xstream.converters.Converter; @@ -57,59 +50,29 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { private TreeMarshallingStrategy delegate = new TreeMarshallingStrategy(); - private ApplicationContext context; - - public DataCenterAwareMarshallingStrategy(ApplicationContext context) { - this.context = context; + public DataCenterAwareMarshallingStrategy() { } @Override public Object unmarshal(Object root, HierarchicalStreamReader reader, DataHolder dataHolder, ConverterLookup converterLookup, Mapper mapper) { - ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup, - this.context); + ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup); return this.delegate.unmarshal(root, reader, dataHolder, wrapped, mapper); } @Override public void marshal(HierarchicalStreamWriter writer, Object obj, ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) { - ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup, - this.context); + ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup); this.delegate.marshal(writer, obj, wrapped, mapper, dataHolder); } - public static class InstanceIdDataCenterInfo implements DataCenterInfo, - UniqueIdentifier { - - private String instanceId; - - public InstanceIdDataCenterInfo(String instanceId) { - this.instanceId = instanceId; - } - - @Override - public Name getName() { - return Name.MyOwn; - } - - @Override - public String getId() { - return this.instanceId; - } - - } - private static class DataCenterAwareConverterLookup implements ConverterLookup { private ConverterLookup delegate; - private ApplicationContext context; - - public DataCenterAwareConverterLookup(ConverterLookup delegate, - ApplicationContext context) { + public DataCenterAwareConverterLookup(ConverterLookup delegate) { this.delegate = delegate; - this.context = context; } @Override @@ -119,57 +82,19 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { return new DataCenterAwareConverter(); } else if (Applications.class == type) { - return new PublishingApplicationsConverter(this.context); + return new ApplicationsConverter(); } return converter; } } - private static class PublishingApplicationsConverter extends ApplicationsConverter { - - private ApplicationContext context; - - public PublishingApplicationsConverter(ApplicationContext context) { - this.context = context; - } - - @Override - public Object unmarshal(HierarchicalStreamReader reader, - UnmarshallingContext unmarshallingContext) { - Object obj = super.unmarshal(reader, unmarshallingContext); - - ProxyFactory factory = new ProxyFactory(obj); - factory.addAdvice(new SetVersionInterceptor(this.context)); - return factory.getProxy(); - } - } - - @CommonsLog - private static class SetVersionInterceptor implements MethodInterceptor { - - private ApplicationContext context; - - public SetVersionInterceptor(ApplicationContext context) { - this.context = context; - } - - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - Object ret = invocation.proceed(); - if ("setVersion".equals(invocation.getMethod().getName())) { - Long version = Long.class.cast(invocation.getArguments()[0]); - log.debug("Applications.setVersion() called with version: " + version); - this.context.publishEvent(new HeartbeatEvent(invocation - .getThis(), version)); - } - return ret; - } - - } - private static class DataCenterAwareConverter extends InstanceInfoConverter { + public DataCenterAwareConverter() { + super(new StringCache()); + } + @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DiscoveryManagerInitializer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DiscoveryManagerInitializer.java deleted file mode 100644 index 2e98cc65..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DiscoveryManagerInitializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2013-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.netflix.eureka; - -import org.springframework.beans.factory.annotation.Autowired; - -import com.netflix.appinfo.ApplicationInfoManager; -import com.netflix.appinfo.EurekaInstanceConfig; -import com.netflix.discovery.DiscoveryManager; -import com.netflix.discovery.EurekaClientConfig; - -/** - * @author Spencer Gibb - */ -public class DiscoveryManagerInitializer { - - @Autowired - private EurekaClientConfig clientConfig; - - @Autowired - private EurekaInstanceConfig instanceConfig; - - public synchronized void init() { - if (DiscoveryManager.getInstance().getDiscoveryClient() == null) { - DiscoveryManager.getInstance().initComponent(this.instanceConfig, - this.clientConfig); - } - if (ApplicationInfoManager.getInstance().getInfo() == null) { - ApplicationInfoManager.getInstance().initComponent(this.instanceConfig); - } - } - -} 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 ea237a38..8dc5e34c 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 @@ -21,7 +21,6 @@ import java.util.concurrent.ConcurrentMap; import javax.annotation.PostConstruct; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -40,7 +39,6 @@ import org.springframework.context.annotation.Configuration; import com.netflix.appinfo.EurekaInstanceConfig; import com.netflix.discovery.EurekaClientConfig; -import com.netflix.discovery.converters.JsonXStream; import com.netflix.discovery.converters.XmlXStream; /** @@ -53,9 +51,6 @@ import com.netflix.discovery.converters.XmlXStream; @AutoConfigureBefore(NoopDiscoveryClientAutoConfiguration.class) public class EurekaClientAutoConfiguration implements ApplicationListener { - @Autowired - private ApplicationContext applicationContext; - private static final ConcurrentMap listenerAdded = new ConcurrentHashMap<>(); @Value("${server.port:${SERVER_PORT:${PORT:8080}}}") @@ -63,10 +58,9 @@ public class EurekaClientAutoConfiguration implements ApplicationListener getInstances(String serviceId) { - List infos = this.discovery.getInstancesByVipAddress(serviceId, + List infos = this.eurekaClient.getInstancesByVipAddress(serviceId, false); - List instances = new ArrayList(); + List instances = new ArrayList<>(); for (InstanceInfo info : infos) { instances.add(new EurekaServiceInstance(info)); } @@ -130,12 +132,12 @@ public class EurekaDiscoveryClient implements DiscoveryClient { @Override public List getServices() { - Applications applications = this.discovery.getApplications(); + Applications applications = this.eurekaClient.getApplications(); if (applications == null) { return Collections.emptyList(); } List registered = applications.getRegisteredApplications(); - List names = new ArrayList(); + List names = new ArrayList<>(); for (Application app : registered) { if (app.getInstances().isEmpty()) { continue; 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 e9f232c9..474f48ae 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 @@ -16,7 +16,6 @@ package org.springframework.cloud.netflix.eureka; -import java.lang.reflect.Field; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -24,6 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PreDestroy; +import lombok.SneakyThrows; import lombok.extern.apachecommons.CommonsLog; import org.springframework.beans.factory.annotation.Autowired; @@ -43,19 +43,15 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.SmartLifecycle; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.core.Ordered; -import org.springframework.util.ReflectionUtils; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; import com.netflix.appinfo.HealthCheckHandler; +import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; -import com.netflix.discovery.DiscoveryManager; +import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; -import com.netflix.discovery.shared.EurekaJerseyClient; /** * @author Dave Syer @@ -73,50 +69,18 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order private AtomicInteger port = new AtomicInteger(0); + @Autowired + private EurekaClientConfig clientConfig; + @Autowired private EurekaInstanceConfigBean instanceConfig; @Autowired(required = false) private HealthCheckHandler healthCheckHandler; - @Autowired - private DiscoveryManagerInitializer discoveryManagerInitializer; - @Autowired private ApplicationContext context; - @PreDestroy - public void close() { - closeDiscoveryClientJersey(); - log.info("Removing application " + this.instanceConfig.getAppname() - + " from eureka"); - DiscoveryManager.getInstance().shutdownComponent(); - } - - private void closeDiscoveryClientJersey() { - log.info("Closing DiscoveryClient.jerseyClient"); - Field jerseyClientField = ReflectionUtils.findField( - com.netflix.discovery.DiscoveryClient.class, "discoveryJerseyClient", - EurekaJerseyClient.JerseyClient.class); - if (jerseyClientField != null) { - try { - jerseyClientField.setAccessible(true); - 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) { - log.error("Error closing DiscoveryClient.jerseyClient", ex); - } - } - } - @Override public void start() { // only set the port if the nonSecurePort is 0 and this.port != 0 @@ -126,18 +90,16 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) { - this.discoveryManagerInitializer.init(); log.info("Registering application " + this.instanceConfig.getAppname() + " with eureka with status " + this.instanceConfig.getInitialStatus()); - ApplicationInfoManager.getInstance().setInstanceStatus( + applicationInfoManager().setInstanceStatus( this.instanceConfig.getInitialStatus()); if (this.healthCheckHandler != null) { - DiscoveryManager.getInstance().getDiscoveryClient() - .registerHealthCheck(this.healthCheckHandler); + eurekaClient().registerHealthCheck(this.healthCheckHandler); } this.context.publishEvent(new InstanceRegisteredEvent<>(this, this.instanceConfig)); @@ -149,8 +111,8 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order public void stop() { log.info("Unregistering application " + this.instanceConfig.getAppname() + " with eureka with status DOWN"); - if (ApplicationInfoManager.getInstance().getInfo() != null) { - ApplicationInfoManager.getInstance().setInstanceStatus( + if (applicationInfoManager().getInfo() != null) { + applicationInfoManager().setInstanceStatus( InstanceStatus.DOWN); } this.running.set(false); @@ -182,23 +144,23 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order return this.order; } - @Configuration - protected static class DiscoveryManagerInitializerConfiguration { - - @Bean - @ConditionalOnMissingBean(DiscoveryManagerInitializer.class) - public DiscoveryManagerInitializer discoveryManagerInitializer() { - return new DiscoveryManagerInitializer(); - } - + @Bean + @ConditionalOnMissingBean(EurekaClient.class) + @SneakyThrows + public EurekaClient eurekaClient() { + return new CloudEurekaClient(applicationInfoManager(), clientConfig, context); } @Bean - @Lazy - @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) - @ConditionalOnMissingBean(com.netflix.discovery.DiscoveryClient.class) - public com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient() { - return DiscoveryManager.getInstance().getDiscoveryClient(); + @ConditionalOnMissingBean(ApplicationInfoManager.class) + public ApplicationInfoManager applicationInfoManager() { + return new ApplicationInfoManager(instanceConfig, instanceInfo()); + } + + @Bean + @ConditionalOnMissingBean(InstanceInfo.class) + public InstanceInfo instanceInfo() { + return new InstanceInfoFactory().create(instanceConfig); } @Bean @@ -231,9 +193,10 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order @Bean @ConditionalOnMissingBean public EurekaHealthIndicator eurekaHealthIndicator( - com.netflix.discovery.DiscoveryClient eurekaDiscoveryClient, EurekaInstanceConfig config) { +EurekaClient eurekaClient, + EurekaInstanceConfig config) { CompositeMetricReader metrics = new CompositeMetricReader(this.metricReaders.toArray(new MetricReader[0])); - return new EurekaHealthIndicator(eurekaDiscoveryClient, metrics, config); + return new EurekaHealthIndicator(eurekaClient, metrics, config); } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthIndicator.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthIndicator.java index 33f38dc9..8c389266 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthIndicator.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaHealthIndicator.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import com.netflix.discovery.EurekaClient; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health.Builder; import org.springframework.boot.actuate.health.Status; @@ -28,7 +29,6 @@ import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.cloud.client.discovery.health.DiscoveryHealthIndicator; import com.netflix.appinfo.EurekaInstanceConfig; -import com.netflix.discovery.DiscoveryClient; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; @@ -37,7 +37,7 @@ import com.netflix.discovery.shared.Applications; */ public class EurekaHealthIndicator implements DiscoveryHealthIndicator { - private final DiscoveryClient discovery; + private final EurekaClient eurekaClient; private final MetricReader metrics; @@ -45,10 +45,10 @@ public class EurekaHealthIndicator implements DiscoveryHealthIndicator { private int failCount = 0; - public EurekaHealthIndicator(DiscoveryClient discovery, MetricReader metrics, + public EurekaHealthIndicator(EurekaClient eurekaClient, MetricReader metrics, EurekaInstanceConfig instanceConfig) { super(); - this.discovery = discovery; + this.eurekaClient = eurekaClient; this.metrics = metrics; this.instanceConfig = instanceConfig; } @@ -67,7 +67,7 @@ public class EurekaHealthIndicator implements DiscoveryHealthIndicator { } private Status getStatus(Builder builder) { - Status status = new Status(this.discovery.getInstanceRemoteStatus().toString(), + Status status = new Status(this.eurekaClient.getInstanceRemoteStatus().toString(), "Remote status from Eureka server"); @SuppressWarnings("unchecked") Metric value = (Metric) this.metrics @@ -89,11 +89,11 @@ public class EurekaHealthIndicator implements DiscoveryHealthIndicator { } private Map getApplications() { - Applications applications = this.discovery.getApplications(); + Applications applications = this.eurekaClient.getApplications(); if (applications == null) { return Collections.emptyMap(); } - Map result = new HashMap(); + Map result = new HashMap<>(); for (Application application : applications.getRegisteredApplications()) { result.put(application.getName(), application.getInstances().size()); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServerConfigBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServerConfigBean.java index 16533774..743779c2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServerConfigBean.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServerConfigBean.java @@ -80,10 +80,14 @@ public class EurekaServerConfigBean implements EurekaServerConfig { private long aSGUpdateIntervalMs = 5 * MINUTES; + private long aSGCacheExpiryTimeoutMs = 10 * MINUTES; // defaults to longer than the asg update interval + private long responseCacheAutoExpirationInSeconds = 180; private long responseCacheUpdateIntervalMs = 30 * 1000; + private boolean useReadOnlyResponseCache = true; + private boolean disableDelta; private long maxIdleThreadInMinutesAgeForStatusReplication = 10; @@ -132,6 +136,8 @@ public class EurekaServerConfigBean implements EurekaServerConfig { private int remoteRegionRegistryFetchInterval = 30; + private int remoteRegionFetchThreadPoolSize = 20; + private String remoteRegionTrustStore = ""; private String remoteRegionTrustStorePassword = "changeit"; @@ -154,6 +160,10 @@ public class EurekaServerConfigBean implements EurekaServerConfig { private boolean logIdentityHeaders = true; + private String listAutoScalingGroupsRoleName = "ListAutoScalingGroups"; + + private boolean enableReplicatedRequestCompression = false; + @Override public boolean shouldEnableSelfPreservation() { return this.enableSelfPreservation; @@ -205,4 +215,13 @@ public class EurekaServerConfigBean implements EurekaServerConfig { return this.logIdentityHeaders; } + @Override + public boolean shouldUseReadOnlyResponseCache() { + return this.useReadOnlyResponseCache; + } + + @Override + public boolean shouldEnableReplicatedRequestCompression() { + return this.enableReplicatedRequestCompression; + } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java new file mode 100644 index 00000000..b5003e5f --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.eureka; + +import com.netflix.appinfo.DataCenterInfo; +import com.netflix.appinfo.UniqueIdentifier; + +/** + * @author Spencer Gibb + */ +public class InstanceIdDataCenterInfo implements DataCenterInfo, + UniqueIdentifier { + + private String instanceId; + + public InstanceIdDataCenterInfo(String instanceId) { + this.instanceId = instanceId; + } + + @Override + public Name getName() { + return Name.MyOwn; + } + + @Override + public String getId() { + return this.instanceId; + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceInfoFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceInfoFactory.java new file mode 100644 index 00000000..6b2c26be --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/InstanceInfoFactory.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.eureka; + +import java.util.Map; + +import lombok.extern.apachecommons.CommonsLog; + +import com.netflix.appinfo.EurekaInstanceConfig; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.LeaseInfo; + +/** + * See com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider + * @author Spencer Gibb + */ +@CommonsLog +public class InstanceInfoFactory { + + public InstanceInfo create(EurekaInstanceConfig config) { + LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder() + .setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds()) + .setDurationInSecs(config.getLeaseExpirationDurationInSeconds()); + + // Builder the instance information to be registered with eureka + // server + InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(); + + builder.setNamespace(config.getNamespace()) + .setAppName(config.getAppname()) + .setAppGroupName(config.getAppGroupName()) + .setDataCenterInfo(config.getDataCenterInfo()) + .setIPAddr(config.getIpAddress()) + .setHostName(config.getHostName(false)) + .setPort(config.getNonSecurePort()) + .enablePort(InstanceInfo.PortType.UNSECURE, + config.isNonSecurePortEnabled()) + .setSecurePort(config.getSecurePort()) + .enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled()) + .setVIPAddress(config.getVirtualHostName()) + .setSecureVIPAddress(config.getSecureVirtualHostName()) + .setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl()) + .setStatusPageUrl(config.getStatusPageUrlPath(), + config.getStatusPageUrl()) + .setHealthCheckUrls(config.getHealthCheckUrlPath(), + config.getHealthCheckUrl(), config.getSecureHealthCheckUrl()) + .setASGName(config.getASGName()); + + // Start off with the STARTING state to avoid traffic + if (!config.isInstanceEnabledOnit()) { + InstanceInfo.InstanceStatus initialStatus = InstanceInfo.InstanceStatus.STARTING; + log.info("Setting initial instance status as: " + initialStatus); + builder.setStatus(initialStatus); + } + else { + log.info("Setting initial instance status as: " + + InstanceInfo.InstanceStatus.UP + + ". This may be too early for the instance to advertise itself as available. " + + "You would instead want to control this via a healthcheck handler."); + } + + // Add any user-specific metadata information + for (Map.Entry mapEntry : config.getMetadataMap().entrySet()) { + String key = mapEntry.getKey(); + String value = mapEntry.getValue(); + builder.add(key, value); + } + + InstanceInfo instanceInfo = builder.build(); + instanceInfo.setLeaseInfo(leaseInfoBuilder.build()); + return instanceInfo; + } +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfigurationTests.java index 5b5f5e02..164b2b1e 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfigurationTests.java @@ -16,6 +16,10 @@ package org.springframework.cloud.netflix.config; +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; + import javax.annotation.PostConstruct; import org.junit.After; @@ -31,11 +35,7 @@ import org.springframework.core.env.ConfigurableEnvironment; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.DiscoveryClient; -import com.netflix.discovery.DiscoveryManager; - -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; +import com.netflix.discovery.EurekaClient; /** * @author Dave Syer @@ -44,7 +44,7 @@ public class DiscoveryClientConfigServiceAutoConfigurationTests { private AnnotationConfigApplicationContext context; - private DiscoveryClient client = Mockito.mock(DiscoveryClient.class); + private EurekaClient client = Mockito.mock(EurekaClient.class); private InstanceInfo info = InstanceInfo.Builder.newBuilder().setAppName("app") .setHostName("foo").setHomePageUrl("/", null).build(); @@ -68,21 +68,20 @@ public class DiscoveryClientConfigServiceAutoConfigurationTests { 1, this.context .getBeanNamesForType(DiscoveryClientConfigServiceAutoConfiguration.class).length); - Mockito.verify(this.client).getNextServerFromEureka("CONFIGSERVER", false); + Mockito.verify(this.client, times(2)).getNextServerFromEureka("CONFIGSERVER", false); Mockito.verify(this.client).shutdown(); ConfigClientProperties locator = this.context .getBean(ConfigClientProperties.class); assertEquals("http://foo:7001/", locator.getRawUri()); - assertEquals("bar", ApplicationInfoManager.getInstance().getInfo().getMetadata() - .get("foo")); + ApplicationInfoManager infoManager = this.context.getBean(ApplicationInfoManager.class); + assertEquals("bar", infoManager.getInfo().getMetadata().get("foo")); } private void setup(String... env) { AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(parent, env); - parent.getDefaultListableBeanFactory().registerSingleton("mockDiscoveryClient", + parent.getDefaultListableBeanFactory().registerSingleton("eurekaClient", this.client); - DiscoveryManager.getInstance().setDiscoveryClient(this.client); parent.register(PropertyPlaceholderAutoConfiguration.class, DiscoveryClientConfigServiceBootstrapConfiguration.class, EnvironmentKnobbler.class, ConfigClientProperties.class); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfigurationTests.java index a9819837..4581208c 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceBootstrapConfigurationTests.java @@ -16,6 +16,9 @@ package org.springframework.cloud.netflix.config; +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; + import org.junit.After; import org.junit.Test; import org.mockito.Mockito; @@ -25,11 +28,7 @@ import org.springframework.cloud.config.client.ConfigClientProperties; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.DiscoveryClient; -import com.netflix.discovery.DiscoveryManager; - -import static org.junit.Assert.assertEquals; -import static org.mockito.BDDMockito.given; +import com.netflix.discovery.EurekaClient; /** * @author Dave Syer @@ -38,7 +37,7 @@ public class DiscoveryClientConfigServiceBootstrapConfigurationTests { private AnnotationConfigApplicationContext context; - private DiscoveryClient client = Mockito.mock(DiscoveryClient.class); + private EurekaClient client = Mockito.mock(EurekaClient.class); private InstanceInfo info = InstanceInfo.Builder.newBuilder().setAppName("app") .setHostName("foo").setHomePageUrl("/", null).build(); @@ -54,7 +53,7 @@ public class DiscoveryClientConfigServiceBootstrapConfigurationTests { public void offByDefault() throws Exception { this.context = new AnnotationConfigApplicationContext( DiscoveryClientConfigServiceBootstrapConfiguration.class); - assertEquals(0, this.context.getBeanNamesForType(DiscoveryClient.class).length); + assertEquals(0, this.context.getBeanNamesForType(EurekaClient.class).length); assertEquals( 0, this.context @@ -104,8 +103,7 @@ public class DiscoveryClientConfigServiceBootstrapConfigurationTests { this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, env); this.context.getDefaultListableBeanFactory().registerSingleton( - "mockDiscoveryClient", this.client); - DiscoveryManager.getInstance().setDiscoveryClient(this.client); + "eurekaClient", this.client); this.context.register(PropertyPlaceholderAutoConfiguration.class, DiscoveryClientConfigServiceBootstrapConfiguration.class, ConfigClientProperties.class); diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaController.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaController.java index 4946a4ed..2d1f5078 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaController.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaController.java @@ -39,7 +39,7 @@ import com.netflix.appinfo.InstanceInfo; import com.netflix.config.ConfigurationManager; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Pair; -import com.netflix.eureka.PeerAwareInstanceRegistry; +import com.netflix.eureka.PeerAwareInstanceRegistryImpl; import com.netflix.eureka.cluster.PeerEurekaNode; import com.netflix.eureka.resources.StatusResource; import com.netflix.eureka.util.StatusInfo; @@ -53,6 +53,11 @@ public class EurekaController { @Value("${eureka.dashboard.path:/}") private String dashboardPath = ""; + private ApplicationInfoManager applicationInfoManager; + + public EurekaController(ApplicationInfoManager applicationInfoManager) { + this.applicationInfoManager = applicationInfoManager; + } @RequestMapping(method = RequestMethod.GET) public String status(HttpServletRequest request, Map model) { @@ -73,7 +78,7 @@ public class EurekaController { @RequestMapping(value = "/lastn", method = RequestMethod.GET) public String lastn(HttpServletRequest request, Map model) { populateBase(request, model); - PeerAwareInstanceRegistry registery = PeerAwareInstanceRegistry.getInstance(); + PeerAwareInstanceRegistryImpl registery = PeerAwareInstanceRegistryImpl.getInstance(); ArrayList> lastNCanceled = new ArrayList<>(); List> list = registery.getLastNCanceledInstances(); for (Pair entry : list) { @@ -114,11 +119,10 @@ public class EurekaController { .getDeploymentEnvironment()); model.put("datacenter", ConfigurationManager.getDeploymentContext() .getDeploymentDatacenter()); - model.put("registry", PeerAwareInstanceRegistry.getInstance()); - model.put("isBelowRenewThresold", PeerAwareInstanceRegistry.getInstance() + model.put("registry", PeerAwareInstanceRegistryImpl.getInstance()); + model.put("isBelowRenewThresold", PeerAwareInstanceRegistryImpl.getInstance() .isBelowRenewThresold() == 1); - DataCenterInfo info = ApplicationInfoManager.getInstance().getInfo() - .getDataCenterInfo(); + DataCenterInfo info = applicationInfoManager.getInfo().getDataCenterInfo(); if (info.getName() == DataCenterInfo.Name.Amazon) { AmazonInfo amazonInfo = (AmazonInfo) info; model.put("amazonInfo", amazonInfo); @@ -131,7 +135,7 @@ public class EurekaController { private void populateNavbar(HttpServletRequest request, Map model) { Map replicas = new LinkedHashMap<>(); - List list = PeerAwareInstanceRegistry.getInstance() + List list = PeerAwareInstanceRegistryImpl.getInstance() .getReplicaNodes(); for (PeerEurekaNode node : list) { try { @@ -147,7 +151,7 @@ public class EurekaController { } private void populateApps(Map model) { - List sortedApplications = PeerAwareInstanceRegistry + List sortedApplications = PeerAwareInstanceRegistryImpl .getInstance().getSortedApplications(); ArrayList> apps = new ArrayList<>(); for (Application app : sortedApplications) { diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java index 26d195aa..e4764442 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java @@ -26,6 +26,7 @@ import javax.servlet.Filter; import javax.ws.rs.Path; import javax.ws.rs.ext.Provider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -44,6 +45,7 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.util.ClassUtils; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import com.netflix.appinfo.ApplicationInfoManager; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.spi.container.servlet.ServletContainer; @@ -58,13 +60,17 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { /** * List of packages containing Jersey resources required by the Eureka server */ - private static String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery", - "com.netflix.eureka" }; + private static String[] EUREKA_PACKAGES = new String[] { + "com.netflix.discovery", + "com.netflix.eureka"}; + + @Autowired + private ApplicationInfoManager applicationInfoManager; @Bean @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true) public EurekaController eurekaController() { - return new EurekaController(); + return new EurekaController(applicationInfoManager); } /** diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java index f9d332ea..1750d94c 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java @@ -33,12 +33,11 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.ProxyFactory; 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.context.properties.EnableConfigurationProperties; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.log4j.Log4JLoggingSystem; +import org.springframework.cloud.netflix.eureka.DataCenterAwareJacksonCodec; import org.springframework.cloud.netflix.eureka.DataCenterAwareMarshallingStrategy; -import org.springframework.cloud.netflix.eureka.DiscoveryManagerInitializer; import org.springframework.cloud.netflix.eureka.EurekaServerConfigBean; import org.springframework.cloud.netflix.eureka.server.advice.LeaseManagerLite; import org.springframework.cloud.netflix.eureka.server.advice.PiggybackMethodInterceptor; @@ -59,13 +58,13 @@ import org.springframework.web.context.ServletContextAware; import com.netflix.blitz4j.DefaultBlitz4jConfig; import com.netflix.blitz4j.LoggingConfiguration; -import com.netflix.discovery.converters.JsonXStream; import com.netflix.discovery.converters.XmlXStream; +import com.netflix.eureka.AbstractInstanceRegistry; import com.netflix.eureka.EurekaBootStrap; import com.netflix.eureka.EurekaServerConfig; import com.netflix.eureka.EurekaServerConfigurationManager; -import com.netflix.eureka.InstanceRegistry; import com.netflix.eureka.PeerAwareInstanceRegistry; +import com.netflix.eureka.PeerAwareInstanceRegistryImpl; /** * @author Dave Syer @@ -125,15 +124,8 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware } } - @Bean - @ConditionalOnMissingBean(DiscoveryManagerInitializer.class) - public DiscoveryManagerInitializer discoveryManagerIntitializer() { - return new DiscoveryManagerInitializer(); - } - @Override public void start() { - discoveryManagerIntitializer().init(); new Thread(new Runnable() { @Override public void run() { @@ -160,14 +152,8 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware XmlXStream .getInstance() .setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy( - EurekaServerInitializerConfiguration.this.applicationContext)); - JsonXStream - .getInstance() - .setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy( - EurekaServerInitializerConfiguration.this.applicationContext)); - // PeerAwareInstanceRegistry.getInstance(); + new DataCenterAwareMarshallingStrategy()); + DataCenterAwareJacksonCodec.init(); EurekaServerInitializerConfiguration.this.applicationContext .publishEvent(new EurekaRegistryAvailableEvent( EurekaServerInitializerConfiguration.this.eurekaServerConfig)); @@ -226,7 +212,7 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware @Autowired private ApplicationContext applicationContext; - private PeerAwareInstanceRegistry instance; + private PeerAwareInstanceRegistryImpl instance; @Bean public LeaseManagerMessageBroker leaseManagerMessageBroker() { @@ -236,7 +222,7 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware @Override public void onApplicationEvent(EurekaRegistryAvailableEvent event) { if (this.instance == null) { - this.instance = PeerAwareInstanceRegistry.getInstance(); + this.instance = PeerAwareInstanceRegistryImpl.getInstance(); safeInit(); replaceInstance(getProxyForInstance()); expectRegistrations(1); @@ -245,13 +231,13 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware private void safeInit() { Method method = ReflectionUtils - .findMethod(InstanceRegistry.class, "postInit"); + .findMethod(AbstractInstanceRegistry.class, "postInit"); ReflectionUtils.makeAccessible(method); ReflectionUtils.invokeMethod(method, this.instance); } private void replaceInstance(Object proxy) { - Field field = ReflectionUtils.findField(PeerAwareInstanceRegistry.class, + Field field = ReflectionUtils.findField(PeerAwareInstanceRegistryImpl.class, "instance"); try { // Awful ugly hack to work around lack of DI in eureka @@ -285,7 +271,7 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware * registrations (when it's zero, even a successful registration won't reset * the rate threshold in InstanceRegistry.register()). */ - Field field = ReflectionUtils.findField(PeerAwareInstanceRegistry.class, + Field field = ReflectionUtils.findField(AbstractInstanceRegistry.class, "expectedNumberOfRenewsPerMin"); try { // Awful ugly hack to work around lack of DI in eureka @@ -304,7 +290,7 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware /** * Additional aspect for intercepting method invocations on * PeerAwareInstanceRegistry. If - * {@link PeerAwareInstanceRegistry#openForTraffic(int)} is called with a zero + * {@link PeerAwareInstanceRegistryImpl#openForTraffic(int)} is called with a zero * argument, it means that leases are not automatically cancelled if the instance * hasn't sent any renewals recently. This happens for a standalone server. It * seems like a bad default, so we set it to the smallest non-zero value we can, diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/event/LeaseManagerMessageBroker.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/event/LeaseManagerMessageBroker.java index 695a8cce..5d802b07 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/event/LeaseManagerMessageBroker.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/event/LeaseManagerMessageBroker.java @@ -26,7 +26,7 @@ import org.springframework.context.ApplicationContext; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.shared.Application; -import com.netflix.eureka.PeerAwareInstanceRegistry; +import com.netflix.eureka.PeerAwareInstanceRegistryImpl; import com.netflix.eureka.lease.Lease; /** @@ -66,7 +66,7 @@ public class LeaseManagerMessageBroker implements LeaseManagerLite boolean isReplication) { log.debug("renew " + appName + " serverId " + serverId + ", isReplication {}" + isReplication); - List applications = PeerAwareInstanceRegistry.getInstance() + List applications = PeerAwareInstanceRegistryImpl.getInstance() .getSortedApplications(); for (Application input : applications) { if (input.getName().equals(appName)) { diff --git a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarClientApplication.java b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarClientApplication.java new file mode 100644 index 00000000..63ace623 --- /dev/null +++ b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarClientApplication.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.sidecar; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Spencer Gibb + */ +@EnableAutoConfiguration +@EnableDiscoveryClient +@Configuration +@RestController +public class SidecarClientApplication { + + @Autowired + public LoadBalancerClient lb; + + @RequestMapping("/") + public ServiceInstance home() { + return lb.choose("sidecarTest"); + } + + public static void main(String[] args) { + new SpringApplicationBuilder() + .properties("spring.application.name=sidecarClient") + .sources(SidecarClientApplication.class) + .run(args); + } +} diff --git a/spring-cloud-netflix-turbine/pom.xml b/spring-cloud-netflix-turbine/pom.xml index 3e7cad3f..59fc3714 100644 --- a/spring-cloud-netflix-turbine/pom.xml +++ b/spring-cloud-netflix-turbine/pom.xml @@ -84,5 +84,10 @@ compile true + + org.mockito + mockito-core + test + diff --git a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscovery.java b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscovery.java index d94b92f1..c854ea33 100644 --- a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscovery.java +++ b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscovery.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import com.netflix.discovery.EurekaClient; import lombok.extern.apachecommons.CommonsLog; import org.springframework.expression.Expression; @@ -34,8 +35,6 @@ import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.config.DynamicPropertyFactory; import com.netflix.config.DynamicStringProperty; -import com.netflix.discovery.DiscoveryClient; -import com.netflix.discovery.DiscoveryManager; import com.netflix.discovery.shared.Application; import com.netflix.turbine.discovery.Instance; import com.netflix.turbine.discovery.InstanceDiscovery; @@ -60,13 +59,11 @@ public class EurekaInstanceDiscovery implements InstanceDiscovery { private static final DynamicStringProperty ApplicationList = DynamicPropertyFactory .getInstance().getStringProperty("turbine.appConfig", ""); + private final EurekaClient eurekaClient; private final Expression clusterNameExpression; - public EurekaInstanceDiscovery(TurbineProperties turbineProperties) { - // Eureka client should already be configured by spring-platform-netflix-core - // initialize eureka client. - // DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), - // new DefaultEurekaClientConfig()); + public EurekaInstanceDiscovery(TurbineProperties turbineProperties, EurekaClient eurekaClient) { + this.eurekaClient = eurekaClient; SpelExpressionParser parser = new SpelExpressionParser(); this.clusterNameExpression = parser.parseExpression(turbineProperties .getClusterNameExpression()); @@ -78,7 +75,7 @@ public class EurekaInstanceDiscovery implements InstanceDiscovery { */ @Override public Collection getInstanceList() throws Exception { - List instances = new ArrayList(); + List instances = new ArrayList<>(); List appNames = parseApps(); if (appNames == null || appNames.size() == 0) { log.info("No apps configured, returning an empty instance list"); @@ -111,14 +108,9 @@ public class EurekaInstanceDiscovery implements InstanceDiscovery { * @throws Exception */ private List getInstancesForApp(String appName) throws Exception { - List instances = new ArrayList(); + List instances = new ArrayList<>(); log.info("Fetching instances for app: " + appName); - DiscoveryClient client = DiscoveryManager.getInstance().getDiscoveryClient(); - if (client == null) { - log.info("Discovery client not ready for: " + appName); - return instances; - } - Application app = client.getApplication(appName); + Application app = eurekaClient.getApplication(appName); if (app == null) { log.warn("Eureka returned null for app: " + appName); } diff --git a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineConfiguration.java b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineConfiguration.java index 3ae895f2..352fd9a1 100644 --- a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineConfiguration.java +++ b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.netflix.turbine; +import com.netflix.discovery.EurekaClient; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @@ -37,6 +39,9 @@ import com.netflix.turbine.streaming.servlet.TurbineStreamServlet; @EnableDiscoveryClient public class TurbineConfiguration implements SmartLifecycle, Ordered { + @Autowired + private EurekaClient eurekaClient; + @Bean public ServletRegistrationBean turbineStreamServlet() { return new ServletRegistrationBean(new TurbineStreamServlet(), "/turbine.stream"); @@ -49,7 +54,7 @@ public class TurbineConfiguration implements SmartLifecycle, Ordered { @Bean public InstanceDiscovery instanceDiscovery() { - return new EurekaInstanceDiscovery(turbineProperties()); + return new EurekaInstanceDiscovery(turbineProperties(), eurekaClient); } private boolean running; @@ -66,9 +71,6 @@ public class TurbineConfiguration implements SmartLifecycle, Ordered { @Override public void start() { - // TODO: figure out ordering, so this is already run by - // EurekaDiscoveryClientConfiguration - // DiscoveryManager.getInstance().initComponent(instanceConfig, clientConfig); PluginsFactory.setClusterMonitorFactory(new SpringAggregatorFactory()); PluginsFactory.setInstanceDiscovery(instanceDiscovery()); TurbineInit.init(); diff --git a/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscoveryTest.java b/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscoveryTest.java index b4b46033..90b26cd5 100644 --- a/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscoveryTest.java +++ b/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/EurekaInstanceDiscoveryTest.java @@ -16,6 +16,9 @@ package org.springframework.cloud.netflix.turbine; +import static org.mockito.Mockito.*; + +import com.netflix.discovery.EurekaClient; import org.junit.Test; import com.netflix.appinfo.InstanceInfo; @@ -29,9 +32,10 @@ public class EurekaInstanceDiscoveryTest { @Test public void testGetClusterName() { + EurekaClient eurekaClient = mock(EurekaClient.class); String appName = "testAppName"; EurekaInstanceDiscovery discovery = new EurekaInstanceDiscovery( - new TurbineProperties()); + new TurbineProperties(), eurekaClient); InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder().setAppName(appName) .build(); String clusterName = discovery.getClusterName(instanceInfo); @@ -40,9 +44,10 @@ public class EurekaInstanceDiscoveryTest { @Test public void testGetClusterNameCustomExpression() { + EurekaClient eurekaClient = mock(EurekaClient.class); TurbineProperties turbineProperties = new TurbineProperties(); turbineProperties.setClusterNameExpression("aSGName"); - EurekaInstanceDiscovery discovery = new EurekaInstanceDiscovery(turbineProperties); + EurekaInstanceDiscovery discovery = new EurekaInstanceDiscovery(turbineProperties, eurekaClient); String asgName = "myAsgName"; InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder() .setAppName("testApp").setASGName(asgName).build(); @@ -52,9 +57,10 @@ public class EurekaInstanceDiscoveryTest { @Test public void testGetClusterNameInstanceMetadataMapExpression() { + EurekaClient eurekaClient = mock(EurekaClient.class); TurbineProperties turbineProperties = new TurbineProperties(); turbineProperties.setClusterNameExpression("metadata['cluster']"); - EurekaInstanceDiscovery discovery = new EurekaInstanceDiscovery(turbineProperties); + EurekaInstanceDiscovery discovery = new EurekaInstanceDiscovery(turbineProperties, eurekaClient); String metadataProperty = "myCluster"; InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder() .setAppName("testApp").add("cluster", metadataProperty).build();