聊聊eureka client的HeartbeatThreadjava
本文主要研究下eureka client的HeartbeatThreadgit
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.javagithub
//... //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在構造器裏頭執行initScheduledTasks方法,裏頭設置了一個HeartbeatThread的調度,間隔時間是renewalIntervalInSecs秒spring
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.javaapp
/** * 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; } }
能夠看到renew調用的是sendHeartBeat方法,若是成功的話,則更新lastSuccessfulHeartbeatTimestamp;若是返回404,則表示須要從新註冊,首先標記dirty,而後調用register方法,若是成功則重置dirty屬性。curl
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.javaui
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(); }
sendHeartBeat方法是一個PUT請求,參數在url中url
實例debug
curl -i -X PUT http://localhost:8761/eureka/apps/test-service/test1?status=UP&lastDirtyTimestamp=1525767406400&overriddenstatus=UP
返回rest
HTTP/1.1 200 Content-Type: application/xml Content-Length: 0 Date: Tue, 08 May 2018 08:17:46 GMT
eureka client在實例化的時候註冊了一個定時任務,每隔renewalIntervalInSecs,向eureka server發送一次心跳。