Spring Cloud: Eureka Client registration/deregistration cycle

krinklesaurus picture krinklesaurus · Mar 26, 2015 · Viewed 8.8k times · Source

To familiarize myself with Spring Cloud's Eureka client/server mechanism, I try to connect a client to the Eureka server and toggle the connection on/off every 5 minutes to see how the Eureka server handles this.

I have two Eureka clients.

The first on is giving me information about the registered applications with this code:

@Autowired
private DiscoveryClient discoveryClient;

@RequestMapping(value = "/services", produces = MediaType.APPLICATION_JSON)
public ResponseEntity<ResourceSupport> applications() {
    ResourceSupport resource = new ResourceSupport();

    Set<String> regions = discoveryClient.getAllKnownRegions();

    for (String region : regions) {
        Applications allApps = discoveryClient.getApplicationsForARegion(region);
        List<Application> registeredApps = allApps.getRegisteredApplications();
        Iterator<Application> it = registeredApps.iterator();

        while (it.hasNext()) {
            Application app = it.next();

             List<InstanceInfo> instancesInfos = app.getInstances();

             if (instancesInfos != null && !instancesInfos.isEmpty()) {
                 //only show one of the instances
                 InstanceInfo info = instancesInfos.get(0);
                 resource.add(new Link(info.getHomePageUrl(), "urls"));
             }
        }
    }

    return new ResponseEntity<ResourceSupport>(resource, HttpStatus.OK);
}

The second Eureka Client register/deregister itself every 5 minutes:

private static final long EUREKA_INTERVAL = 5 * 60000;

public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(MyServiceApplication.class);

    long currentTime = System.currentTimeMillis();
    long lastToggleTime = System.currentTimeMillis();
    boolean connected = true;
    while (true) {
        if (currentTime - lastToggleTime > EUREKA_INTERVAL) {
            if (connected) {
                System.err.println("disconnect");
                DiscoveryManager.getInstance().shutdownComponent();
                connected = false;
                lastToggleTime = System.currentTimeMillis();
            }
            else {
                System.err.println("connect");
                DiscoveryManager.getInstance().initComponent(
                        DiscoveryManager.getInstance().getEurekaInstanceConfig(),
                        DiscoveryManager.getInstance().getEurekaClientConfig());
                connected = true;
                lastToggleTime = System.currentTimeMillis();
            }
        }
        currentTime = System.currentTimeMillis();
    }

}

The log output of the second Eureka client looks as follows:

disconnect
2015-03-26 13:59:23.713  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_MYAPPNAME/MYMEGAHOSTNAME - deregister  status: 200
connect
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Application is null : false
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2015-03-26 14:04:23.870  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Application version is -1: true
2015-03-26 14:04:23.889  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2015-03-26 14:04:23.892  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : The response status is 200
2015-03-26 14:04:23.894  INFO 3452 --- [           main] com.netflix.discovery.DiscoveryClient    : Starting heartbeat executor: renew interval is: 30
2015-03-26 14:04:53.916  INFO 3452 --- [ool-11-thread-1] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_MYAPPNAME/MYMEGAHOSTNAME - Re-registering apps/MYAPPNAME
2015-03-26 14:04:53.916  INFO 3452 --- [ool-11-thread-1] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_MYAPPNAME/MYMEGAHOSTNAME: registering service...
2015-03-26 14:04:53.946  INFO 3452 --- [ool-11-thread-1] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_MYAPPNAME/MYMEGAHOSTNAME - registration status: 204

When starting both Eureka clients for the first time, this works well. The second client is shown by the first client AND the second client is visible in die Eureka server console. When the second client disconnects itself from the Eureka server, it is no longer listed there and the first client is also not showing it anymore.

Unfortunately, when the second client reconnects to the Eureka server, the Eureka server console is just showing a big red highlighted "DOWN (1)" and the first client is not showing the second client anymore. What am I missing here?





Solution:

Based on Dave Sayer's answer my solution was to add a custom @Configuration that has the EurekaDiscoveryClientConfiguration autowired and starts a thread for toggling the registration. Note that this is for test purposes only, so it may be a quite ugly solution ;-)

@Configuration
static public class MyDiscoveryClientConfigServiceAutoConfiguration {

    @Autowired
    private EurekaDiscoveryClientConfiguration lifecycle;

    @PostConstruct
    public void init() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                long currentTime = System.currentTimeMillis();
                long lastToggleTime = System.currentTimeMillis();
                boolean connected = true;

                while (true) {
                    if (currentTime - lastToggleTime > EUREKA_INTERVAL) {
                        if (connected) {
                            System.err.println("disconnect");

                            lifecycle.stop();
                            DiscoveryManager.getInstance().getDiscoveryClient().shutdown();
                            connected = false;
                            lastToggleTime = System.currentTimeMillis();
                        }
                        else {
                            System.err.println("connect");
                            DiscoveryManager.getInstance().initComponent(
                                    DiscoveryManager.getInstance().getEurekaInstanceConfig(),
                                    DiscoveryManager.getInstance().getEurekaClientConfig());

                            lifecycle.start();
                            connected = true;
                            lastToggleTime = System.currentTimeMillis();
                        }
                    }
                    currentTime = System.currentTimeMillis();
                }
            }
        }).start();
    }

}

Answer

Dave Syer picture Dave Syer · Mar 26, 2015

Your call to DiscoveryManager.getInstance().initComponent() does not set the status (and the default is DOWN). In Spring Cloud we handle it in a special EurekaDiscoveryClientConfiguration.start() lifecycle. You could inject that and re-use it like this:

@Autowired
private EurekaDiscoveryClientConfiguration lifecycle;

@PostConstruct
public void init() {
    this.lifecycle.stop();
    if (DiscoveryManager.getInstance().getDiscoveryClient() != null) {
        DiscoveryManager.getInstance().getDiscoveryClient().shutdown();
    }
    ApplicationInfoManager.getInstance().initComponent(this.instanceConfig);
    DiscoveryManager.getInstance().initComponent(this.instanceConfig,
            this.clientConfig);
    this.lifecycle.start();
}

(which is code taken from here: https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/config/DiscoveryClientConfigServiceAutoConfiguration.java#L58).