题主的问题描述太绕了,我们先把集群中的角色定义下:
Eureka架构
比较细节的架构图如下所示:
在配置多个EurekaServer的Service Provider,每次Service Provider启动的时候会选择一个Eureka Server,之后如果这个Eureka Server挂了,才会切换Eureka Server,在当前使用的Eureka Server挂掉之前,不会切换。
被Service Provider选择用来发送请求Eureka Server其实比其他Server多了一项工作,就是发客户端发来的请求,转发到集群中其他的Eureka Server。其实这个压力并没有太大,但是如果集群中实例个数比较多,或者心跳间隔比较短的情况下,的确有不小的压力。可以考虑每个服务配置的Eureka Server顺序不一样。
但是其实仔细想想,只是个请求转发,能有多大压力啊。。。。
最后,我们详细分析下服务注册与取消的源代码(可以直接参考下我的博客关于Eureka的系列分析张哈希的博客 - CSDN博客blog.csdn.net
):
关于服务注册开启/关闭服务注册配置:eureka.client.register-with-eureka = true (默认)
什么时候注册?应用第一次启动时,初始化EurekaClient时,应用状态改变:从STARTING变为UP会触发这个Listener,调用instanceInfoReplicator.onDemandUpdate(); 可以推测出,实例状态改变时,也会通过注册接口更新实例状态信息
statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
@Override
public String getId() {
return "statusChangeListener";
}
@Override
public void notify(StatusChangeEvent statusChangeEvent) {
if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
// log at warn level if DOWN was involved
logger.warn("Saw local status change event {}", statusChangeEvent);
} else {
logger.info("Saw local status change event {}", statusChangeEvent);
}
instanceInfoReplicator.onDemandUpdate();
}
};定时任务,如果InstanceInfo发生改变,也会通过注册接口更新信息
public void run() {
try {
discoveryClient.refreshInstanceInfo();
//如果实例信息发生改变,则需要调用register更新InstanceInfo
Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info replicator", t);
} finally {
Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}在定时renew时,如果renew接口返回404(代表这个实例在EurekaServer上面找不到),可能是之前注册失败或者注册过期导致的。这时需要调用register重新注册
boolean renew() {
EurekaHttpResponse httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug("{} - Heartbeat status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
//如果renew接口返回404(代表这个实例在EurekaServer上面找不到),可能是之前注册失败或者注册过期导致的
if (httpResponse.getStatusCode() == 404) {
REREGISTER_COUNTER.increment();
logger.info("{} - Re-registering apps/{}", PREFIX + 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("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e);
return false;
}
}
向Eureka发送注册请求EurekaServer发生了什么?
主要有两个存储,一个是之前提到过的registry,还有一个最近变化队列,后面我们会知道,这个最近变化队列里面就是客户端获取增量实例信息的内容:
# 整体注册信息缓存
private final ConcurrentHashMap>> registry = new ConcurrentHashMap>>();
# 最近变化队列
private ConcurrentLinkedQueue recentlyChangedQueue = new ConcurrentLinkedQueue();
EurekaServer收到实例注册主要分两步:调用父类方法注册
同步到其他EurekaServer实例
public void register(InstanceInfo info, boolean isReplication) {
int leaseDuration = 90;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
//调用父类方法注册
super.register(info, leaseDuration, isReplication);
//同步到其他EurekaServer实例
this.replica