Browse Source
Implements forwarding of eureka CacheRefreshedEvent as HeartbeatEvent, rather thank serialization hack. Uses new Eureka DI functionality (mostly there, some issues). Updates EurekaJacksonCodec to be "DataCenterAware". Reference EurekaClient interface rather than DiscoveryClient class. fixes gh-349pull/6/head
28 changed files with 572 additions and 313 deletions
@ -0,0 +1,54 @@
@@ -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)); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,144 @@
@@ -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<Class<?>, 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<Class<?>, 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> 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; |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,47 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,44 @@
@@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,87 @@
@@ -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<String, String> 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; |
||||
} |
||||
} |
@ -0,0 +1,52 @@
@@ -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); |
||||
} |
||||
} |
Loading…
Reference in new issue