Talk about eurekclient’s HeartbeatThread

  springcloud

Order

This paper mainly studies the HeartbeatThread of eurekclient.

DiscoveryClient.initScheduledTasks

eureka-client-1.8.8-sources.jar! /com/netflix/discovery/DiscoveryClient.java

        //...
        //int DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND = 10;
        int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
        scheduler = Executors.newScheduledThreadPool(2,
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-%d")
                            .setDaemon(true)
                            .build());

        heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),  //DEFAULT_EXECUTOR_THREAD_POOL_SIZE = 5
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff

        //......
        if (clientConfig.shouldRegisterWithEureka()) {
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);
                    //......
        }

DiscoveryClient executes the initScheduledTasks method in the constructor, which sets a schedule of HeartbeatThread with an interval of renewalIntervalInSecs seconds.

HeartbeatThread

eureka-client-1.8.8-sources.jar! /com/netflix/discovery/DiscoveryClient.java

    /**
     * The heartbeat task that renews the lease in the given intervals.
     */
    private class HeartbeatThread implements Runnable {

        public void run() {
            if (renew()) {
                lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
            }
        }
    }

    /**
     * Renew with the eureka service by making the appropriate REST call
     */
    boolean renew() {
        EurekaHttpResponse<InstanceInfo> httpResponse;
        try {
            httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
            logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
            if (httpResponse.getStatusCode() == 404) {
                REREGISTER_COUNTER.increment();
                logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
                long timestamp = instanceInfo.setIsDirtyWithTime();
                boolean success = register();
                if (success) {
                    instanceInfo.unsetIsDirty(timestamp);
                }
                return success;
            }
            return httpResponse.getStatusCode() == 200;
        } catch (Throwable e) {
            logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
            return false;
        }
    }

It can be seen that renew calls the sendHeartBeat method, and if successful, updates LastUCCESSFULHeartBeatTimestamp; If 404 is returned, it means that re-registration is required. First, dirty is marked, then the register method is called, and if successful, the dirty property is reset.

RestTemplateEurekaHttpClient.sendHeartBeat

spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar! /org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java

    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
            InstanceInfo info, InstanceStatus overriddenStatus) {
        String urlPath = serviceUrl + "apps/" + appName + '/' + id + "?status="
                + info.getStatus().toString() + "&lastDirtyTimestamp="
                + info.getLastDirtyTimestamp().toString() + (overriddenStatus != null
                        ? "&overriddenstatus=" + overriddenStatus.name() : "");

        ResponseEntity<InstanceInfo> response = restTemplate.exchange(urlPath,
                HttpMethod.PUT, null, InstanceInfo.class);

        EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(
                response.getStatusCodeValue(), InstanceInfo.class)
                        .headers(headersOf(response));

        if (response.hasBody())
            eurekaResponseBuilder.entity(response.getBody());

        return eurekaResponseBuilder.build();
    }

The sendHeartBeat method is a PUT request with parameters in the url.

Example

curl -i -X PUT http://localhost:8761/eureka/apps/test-service/test1?status=UP&lastDirtyTimestamp=1525767406400&overriddenstatus=UP

Return

HTTP/1.1 200
Content-Type: application/xml
Content-Length: 0
Date: Tue, 08 May 2018 08:17:46 GMT

Summary

Eureka client registered a timed task at the time of instantiation and sent a heartbeat to eureka server every other time.

doc