Created
August 30, 2018 14:24
-
-
Save michael-pratt/30517da70da3e00a902a40116e2f87f8 to your computer and use it in GitHub Desktop.
Registering multiple instances in Eureka from one application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.myorg; | |
import com.netflix.appinfo.ApplicationInfoManager; | |
import com.netflix.appinfo.InstanceInfo; | |
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; | |
import com.netflix.discovery.DiscoveryClient; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.boot.context.event.ApplicationReadyEvent; | |
import org.springframework.cloud.commons.util.InetUtils; | |
import org.springframework.cloud.commons.util.InetUtilsProperties; | |
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; | |
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; | |
import org.springframework.cloud.netflix.eureka.InstanceInfoFactory; | |
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry; | |
import org.springframework.context.ApplicationContext; | |
import org.springframework.context.ApplicationListener; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.event.ContextClosedEvent; | |
import java.util.HashSet; | |
import java.util.Set; | |
import java.util.UUID; | |
@SpringBootApplication | |
public class MultipleEurekaInstanceApplication | |
{ | |
private final static Logger ourLogger = | |
LoggerFactory.createLogger(MultipleEurekaInstanceApplication.class); | |
/** | |
* Eureka application name / instance prefix | |
*/ | |
private final static String APPLICATION_NAME = "my-app"; | |
@Autowired | |
private EurekaInstanceConfigBean originalInstanceConfig; | |
@Autowired | |
private EurekaClientConfigBean originalClientConfig; | |
@Autowired | |
private ApplicationInfoManager applicationInfoManager; | |
@Autowired | |
private ApplicationContext context; | |
@Autowired | |
private AbstractDiscoveryClientOptionalArgs args; | |
@Value("${HOSTNAME}") | |
private String hostName; | |
@Value("${my.app.numInstances:5}") | |
private int numInstances; | |
/** | |
* These are the DiscoverClient objects we manage ourselves | |
*/ | |
private Set<DiscoveryClient> discoveryClients = new HashSet<>(); | |
public static void main(String[] args) | |
{ | |
SpringApplication.run(MultipleEurekaInstanceApplication.class, args); | |
} | |
/** | |
* Responsible for registering <code>my.app.numInstances</code> number | |
* of instances in Eureka for use by session manager. Waits until the | |
* application context is ready to create the registrations. | |
* | |
* @return | |
*/ | |
@Bean | |
public ApplicationListener applicationListener() | |
{ | |
return applicationEvent -> { | |
if(applicationEvent instanceof ApplicationReadyEvent) | |
{ | |
if(numInstances < 1) | |
{ | |
numInstances = 1; | |
} | |
// | |
// We get 1 session for free with Spring Cloud Eureka, so we only need | |
// to register (numInstances - 1) new instances. | |
// | |
for(int i = 1; i < numInstances; i++) | |
{ | |
// | |
// Simply creating the DiscoveryClient is enough to force | |
// registration. We hold on to the reference for later use | |
// so we can unregister during shutdown. | |
// | |
DiscoveryClient discoveryClient = | |
new DiscoveryClient( | |
duplicateAppInfoManager(), | |
duplicateConfig(), | |
args); | |
discoveryClients.add(discoveryClient); | |
} | |
} | |
}; | |
} | |
/** | |
* Handles forcing all the extra instances we register to DOWN state when the | |
* application is terminated. | |
*/ | |
@Bean | |
public ApplicationListener discoveryClientShutdownListener() | |
{ | |
return applicationEvent -> { | |
if(applicationEvent instanceof ContextClosedEvent) | |
{ | |
for(DiscoveryClient client: discoveryClients) | |
{ | |
ourLogger.info("Shutting down discovery client for " + client.getApplicationInfoManager().getEurekaInstanceConfig().getInstanceId()); | |
client.shutdown(); | |
} | |
} | |
}; | |
} | |
// | |
// These duplicateXXX methods are used to create copies of the | |
// various Eureka beans that the Spring Cloud auto config | |
// creates for us. | |
// | |
private ApplicationInfoManager duplicateAppInfoManager() | |
{ | |
EurekaInstanceConfigBean newInstanceConfig = | |
new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties())); | |
newInstanceConfig.setEnvironment(context.getEnvironment()); | |
newInstanceConfig.setAppname(APPLICATION_NAME); | |
newInstanceConfig.setInstanceId(APPLICATION_NAME + ":" + hostName + ":" + UUID.randomUUID().toString()); | |
newInstanceConfig.setInitialStatus(InstanceInfo.InstanceStatus.UP); | |
newInstanceConfig.setNonSecurePortEnabled(originalInstanceConfig.isNonSecurePortEnabled()); | |
newInstanceConfig.setNonSecurePort(originalInstanceConfig.getNonSecurePort()); | |
newInstanceConfig.setSecurePortEnabled(originalInstanceConfig.isSecurePortEnabled()); | |
newInstanceConfig.setSecurePort(originalInstanceConfig.getSecurePort()); | |
newInstanceConfig.setDataCenterInfo(originalInstanceConfig.getDataCenterInfo()); | |
newInstanceConfig.setHealthCheckUrl(originalInstanceConfig.getHealthCheckUrl()); | |
newInstanceConfig.setSecureHealthCheckUrl(originalInstanceConfig.getSecureHealthCheckUrl()); | |
newInstanceConfig.setHomePageUrl(originalInstanceConfig.getHomePageUrl()); | |
newInstanceConfig.setStatusPageUrl(originalInstanceConfig.getStatusPageUrl()); | |
newInstanceConfig.setStatusPageUrlPath(originalInstanceConfig.getStatusPageUrlPath()); | |
newInstanceConfig.setIpAddress(originalInstanceConfig.getIpAddress()); | |
newInstanceConfig.setPreferIpAddress(originalInstanceConfig.isPreferIpAddress()); | |
ApplicationInfoManager manager = | |
new ApplicationInfoManager( | |
newInstanceConfig, | |
duplicateInstanceInfo(applicationInfoManager.getInfo())); | |
return manager; | |
} | |
private InstanceInfo duplicateInstanceInfo(InstanceInfo info) | |
{ | |
// | |
// Temporarily swap the instance ID; have to restore so that the bean | |
// created in the auto configs keeps it original ID. | |
// | |
String originalId = originalInstanceConfig.getInstanceId(); | |
originalInstanceConfig.setInstanceId(SESSION_SERVER_NAME + ":" + hostName + ":" + UUID.randomUUID().toString()); | |
InstanceInfo newInfo = new InstanceInfoFactory().create(originalInstanceConfig); | |
newInfo.setStatus(InstanceInfo.InstanceStatus.UP); | |
originalInstanceConfig.setInstanceId(originalId); | |
return newInfo; | |
} | |
private EurekaClientConfigBean duplicateConfig() | |
{ | |
EurekaClientConfigBean newConfig = new EurekaClientConfigBean(); | |
newConfig.setFetchRegistry(false); | |
newConfig.setEurekaServerPort(originalClientConfig.getEurekaServerPort()); | |
newConfig.setAllowRedirects(originalClientConfig.isAllowRedirects()); | |
newConfig.setAvailabilityZones(originalClientConfig.getAvailabilityZones()); | |
newConfig.setBackupRegistryImpl(originalClientConfig.getBackupRegistryImpl()); | |
newConfig.setServiceUrl(originalClientConfig.getServiceUrl()); | |
return newConfig; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This assumes you have Spring Cloud Eureka configured to register an instance automatically: