From d0777af76182ba9a0ba8bc483b9a9fd6bb281bd0 Mon Sep 17 00:00:00 2001 From: Puneet Oberai Date: Tue, 3 Sep 2013 15:38:59 -0700 Subject: [PATCH] Adding in eureka based host supplier - issue 394 --- astyanax-contrib/conf/log4j.xml | 13 +++ .../eureka/EurekaBasedHostSupplier.java | 103 ++++++++++++++++++ build.gradle | 9 ++ settings.gradle | 2 +- 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 astyanax-contrib/conf/log4j.xml create mode 100644 astyanax-contrib/src/main/java/com/netflix/astyanax/contrib/eureka/EurekaBasedHostSupplier.java diff --git a/astyanax-contrib/conf/log4j.xml b/astyanax-contrib/conf/log4j.xml new file mode 100644 index 000000000..6d1e15624 --- /dev/null +++ b/astyanax-contrib/conf/log4j.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/astyanax-contrib/src/main/java/com/netflix/astyanax/contrib/eureka/EurekaBasedHostSupplier.java b/astyanax-contrib/src/main/java/com/netflix/astyanax/contrib/eureka/EurekaBasedHostSupplier.java new file mode 100644 index 000000000..4c810bb4a --- /dev/null +++ b/astyanax-contrib/src/main/java/com/netflix/astyanax/contrib/eureka/EurekaBasedHostSupplier.java @@ -0,0 +1,103 @@ +package com.netflix.astyanax.contrib.eureka; + +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.netflix.appinfo.AmazonInfo; +import com.netflix.appinfo.AmazonInfo.MetaDataKey; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.MyDataCenterInstanceConfig; +import com.netflix.astyanax.connectionpool.Host; +import com.netflix.discovery.DefaultEurekaClientConfig; +import com.netflix.discovery.DiscoveryClient; +import com.netflix.discovery.DiscoveryManager; +import com.netflix.discovery.shared.Application; + +/** + * Simple class that implements {@link Supplier}<{@link List}<{@link Host}>>. It provides a List<{@link Host}> + * using the {@link DiscoveryManager} which is the eureka client. + * + * Note that the class needs the eureka application name to discover all instances for that application. + * + * @author poberai + */ +public class EurekaBasedHostSupplier implements Supplier> { + + private static final Logger LOG = LoggerFactory.getLogger(EurekaBasedHostSupplier.class); + + // The C* cluster name for discovering nodes + private final String applicationName; + + public EurekaBasedHostSupplier(String applicationName) { + this.applicationName = applicationName.toUpperCase(); + // initialize eureka client. make sure eureka properties are properly configured in config.properties + DiscoveryManager.getInstance().initComponent(new MyDataCenterInstanceConfig(), new DefaultEurekaClientConfig()); + } + + @Override + public List get() { + + DiscoveryClient discoveryClient = DiscoveryManager.getInstance().getDiscoveryClient(); + if (discoveryClient == null) { + LOG.error("Error getting discovery client"); + throw new RuntimeException("Failed to create discovery client"); + } + + Application app = discoveryClient.getApplication(applicationName); + List hosts = Lists.newArrayList(); + + if (app == null) { + return hosts; + } + + List ins = app.getInstances(); + + if (ins == null || ins.isEmpty()) { + return hosts; + } + + hosts = Lists.newArrayList(Collections2.transform( + Collections2.filter(ins, new Predicate() { + @Override + public boolean apply(InstanceInfo input) { + return input.getStatus() == InstanceInfo.InstanceStatus.UP; + } + }), new Function() { + @Override + public Host apply(InstanceInfo info) { + String[] parts = StringUtils.split( + StringUtils.split(info.getHostName(), ".")[0], '-'); + + Host host = new Host(info.getHostName(), info.getPort()) + .addAlternateIpAddress( + StringUtils.join(new String[] { parts[1], parts[2], parts[3], + parts[4] }, ".")) + .addAlternateIpAddress(info.getIPAddr()) + .setId(info.getId()); + + try { + if (info.getDataCenterInfo() instanceof AmazonInfo) { + AmazonInfo amazonInfo = (AmazonInfo)info.getDataCenterInfo(); + host.setRack(amazonInfo.get(MetaDataKey.availabilityZone)); + } + } + catch (Throwable t) { + LOG.error("Error getting rack for host " + host.getName(), t); + } + + return host; + } + })); + + + return hosts; + } +} diff --git a/build.gradle b/build.gradle index 42f75a603..503666c79 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,7 @@ project(':astyanax') { compile project(':astyanax-recipes') compile project(':astyanax-entity-mapper') compile project(':astyanax-examples') + compile project(':astyanax-contrib') } } @@ -130,3 +131,11 @@ project(':astyanax-examples') { testCompile project(':astyanax-thrift') } } + +project(':astyanax-contrib') { + apply plugin: 'java' + dependencies { + compile project(':astyanax-core') + compile 'com.netflix.eureka:eureka-client:1.1.110' + } +} diff --git a/settings.gradle b/settings.gradle index 11c28240f..685030644 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ rootProject.name='astyanax' -include 'astyanax-core', 'astyanax-queue', 'astyanax-thrift', 'astyanax-recipes', 'astyanax-cassandra', 'astyanax-entity-mapper', 'astyanax-examples', 'astyanax' +include 'astyanax-core', 'astyanax-queue', 'astyanax-thrift', 'astyanax-recipes', 'astyanax-cassandra', 'astyanax-entity-mapper', 'astyanax-examples', 'astyanax-contrib', 'astyanax'