spring-cloudnetflix-eureka

How to register a Eureka client with multiple names?


to give you the entire context of the issue, I have the following scenario.

Currently I have three spring boot applications (SB 2.7.15): a, b and c. In order to reduce the footprint, we decided to create one single application, x, that is merging the features of the other three. To not have complains and to privide a very smooth transition to our clients, we decided to register the new application with the domain of the other 3 and his own domain and we also want to register the new app in eureka with the 4 names (a, b, c and x) and the users be able to take some time to adjust their aplications.

The easies way I have found so far is to have 4 instances of the application x, each of them registeres using a different name (a, b, c and x), but I do not like so much this approach.

Did you face this scenario or do you have any ideea how I can do it?

Currently I'm trying to programaticaly create a DiscoveryClient for each application name. They have to be idendical excepting the appName and the host mostly. This approach looks a bit insecure and not sure if I should continue on this thread.


Solution

  • I finally resolved the issue with the following configuration class:

    @Slf4j
    @RequiredArgsConstructor
    @Configuration
    @ConditionalOnProperty(value = "eureka.client.enabled", havingValue = "true", matchIfMissing = true)
    public class MultipleEurekaInstanceConfiguration {
    
      private final EurekaInstanceConfigBean originalInstanceConfig;
      private final EurekaClientConfigBean originalClientConfig;
      private final ApplicationContext context;
      private final AbstractDiscoveryClientOptionalArgs args;
    
      private final Set<DiscoveryClient> discoveryClients = new HashSet<>();
    
      @Value("#{'${eureka.additional-names}'.split(',')}")
      private final Set<String> appsName;
    
      @Bean
      public List<String> eurekaAdditionalNames() {
        return List.copyOf(appsName);
      }
    
      @EventListener(ApplicationReadyEvent.class)
      public void registerDiscoveryClients() {
        WebClientTransportClientFactories webClientTransportClientFactories = new WebClientTransportClientFactories(
            WebClient::builder);
        appsName.stream().filter(StringUtils::isNotBlank).forEach(appName -> {
          var discoveryClient = new DiscoveryClient(duplicateAppInfoManager(appName), duplicateConfig(),
              webClientTransportClientFactories, args);
          discoveryClients.add(discoveryClient);
        });
      }
    
      @EventListener(ContextClosedEvent.class)
      public void discoveryClientShutdownListener() {
        for (DiscoveryClient client : discoveryClients) {
          log.info("Shutting down discovery client for " + client.getApplicationInfoManager()
              .getEurekaInstanceConfig().getInstanceId());
          client.shutdown();
        }
      }
    
      private ApplicationInfoManager duplicateAppInfoManager(String appName) {
        var newInstanceConfig = new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties()));
        String originalInstanceId = originalInstanceConfig.getInstanceId();
        String originalAppName = originalInstanceConfig.getAppname();
    
        BeanUtils.copyProperties(originalInstanceConfig, newInstanceConfig);
    
        newInstanceConfig.setEnvironment(context.getEnvironment());
        newInstanceConfig.setAppname(appName);
        newInstanceConfig.setInstanceId(originalInstanceId.replace(originalAppName, appName));
        newInstanceConfig.setInitialStatus(InstanceInfo.InstanceStatus.UP);
        newInstanceConfig.setSecureVirtualHostName(appName);
        newInstanceConfig.setVirtualHostName(appName);
    
        return new ApplicationInfoManager(newInstanceConfig, duplicateInstanceInfo(newInstanceConfig));
      }
    
      private InstanceInfo duplicateInstanceInfo(EurekaInstanceConfigBean instanceConfig) {
        InstanceInfo newInfo = new InstanceInfoFactory().create(instanceConfig);
        newInfo.setStatus(InstanceInfo.InstanceStatus.UP);
        return newInfo;
      }
    
      private EurekaClientConfigBean duplicateConfig() {
        var newConfig = new EurekaClientConfigBean();
        BeanUtils.copyProperties(originalClientConfig, newConfig);
        newConfig.setFetchRegistry(false);
        return newConfig;
      }
    }
    

    registerDiscoveryClients method is adding additional eureka clients with the same configuration but a different name. The additional names are fetched from eureka.additional-names property. discoveryClientShutdownListener method is removing from eureka the clients added in the registerDiscoveryClients method.