Eureka服务注册发现原理
客户端启动时如何注册到服务端
Eureka客户端在启动时,首先会创建心跳的定时任务,默认每隔30s向向服务端发送心跳请求,服务端对心跳做出响应。如果响应状态码是404,表示服务端并没有该客户端的服务信息,那么客户端就会向服务端发送注册请求,注册信息包括服务名、IP端口、实例ID等信息。
//DiscoveryClient.java
//定时任务线程池默认有两个任务,一个用来心跳检测,一个用来刷新缓存
scheduler = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-%d").setDaemon(true).build());
//心跳检测线程池:核心线程1个,EurekaClientConfigBean中默认配置最大心跳线程2个
heartbeatExecutor = new ThreadPoolExecutor(1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
.setDaemon(true)
.build()
);
//缓存刷新线程池
cacheRefreshExecutor = new ThreadPoolExecutor(1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
.setDaemon(true)
.build()
);
//...
//初始化所有的调度任务
initScheduledTasks();
//...
//DiscoveryClient#initScheduledTasks
//客户端默认shouldFetchRegistry为true,注册缓存刷新定时器,定时刷新缓存
//默认shouldRegistryWithEureka为true,定时调用心跳线程
private class HeartbeatThread implements Runnable {
public void run() {
if (renew()) {
lastSuccessfulHeartbeatTimeStamp = = System.currentTimeMillis();
}
}
}
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() == Status.NOT_FOUND.getStatusCode()) {
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() == Status.OK.getStatusCode();
} catch (Throwable e) {
logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
return false;
}
}
服务端存储客户端信息
//AbstractInstanceRegistry#register方法把服务实例信息InstanceInfo注册到了ConcurrentHashMap中
###客户端获取服务端信息
客户端通过刷新缓存的定时任务拉取服务端的服务信息,每次拉取后刷新本地已保存的信息。
心跳机制和服务剔除机制
心跳机制:客户端启动后,定时任务默认每隔30s向服务端发送心跳数据保持活跃。
服务剔除机制:如果开启了自我保护机制,所有的服务都不会被剔除;如果未开启自我保护机制,那么将判断最后一分钟收到的心跳数与前一分钟收到的心跳数临界值比较,如果前者大于后者,且后者大于零则启用服务剔除机制。一旦开启了服务剔除机制,Eureka服务端并不会直接剔除所有已过期的服务,而是通过随机数的方式进行剔除,避免自我保护开启之前将所有的服务给剔除。