序
本文主要研究下eureka client的HeartbeatThread
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(), //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秒
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 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属性。
RestTemplateEurekaHttpClient.sendHeartBeat
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java
public EurekaHttpResponse 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 response = restTemplate.exchange(urlPath,
HttpMethod.PUT, null, InstanceInfo.class);
EurekaHttpResponseBuilder eurekaResponseBuilder = anEurekaHttpResponse(
response.getStatusCodeValue(), InstanceInfo.class)
.headers(headersOf(response));
if (response.hasBody())
eurekaResponseBuilder.entity(response.getBody());
return eurekaResponseBuilder.build();
}
sendHeartBeat方法是一个PUT请求,参数在url中
实例
curl -i -X PUT http://localhost:8761/eureka/apps/test-service/test1?status=UP&lastDirtyTimestamp=1525767406400&overriddenstatus=UP
返回
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发送一次心跳。
doc
- Understanding-eureka-client-server-communication