今天来看看eureka server集群相关的东西。
在eureka server初始化的过程中是否还记得PeerEurekaNodes这个类
//eureka-server集群初始化
PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
registry,
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
applicationInfoManager
);
会执行PeerEurekaNodes.start()方法,启动一个后台的线程,默认是每隔10分钟,运行一个任务,基于配置文件中的url来刷新eureka server列表。
public void start() {
taskExecutor = Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
thread.setDaemon(true);
return thread;
}
}
);
try {
updatePeerEurekaNodes(resolvePeerUrls());
Runnable peersUpdateTask = new Runnable() {
@Override
public void run() {
try {
updatePeerEurekaNodes(resolvePeerUrls());
} catch (Throwable e) {
logger.error("Cannot update the replica Nodes", e);
}
}
};
taskExecutor.scheduleWithFixedDelay(
peersUpdateTask,
serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
TimeUnit.MILLISECONDS
);
} catch (Exception e) {
throw new IllegalStateException(e);
}
for (PeerEurekaNode node : peerEurekaNodes) {
logger.info("Replica node URL: {}", node.getServiceUrl());
}
}
@Override
public int getPeerEurekaNodesUpdateIntervalMs() {
return configInstance
.getIntProperty(namespace + "peerEurekaNodesUpdateIntervalMs",
(10 * 60 * 1000)).get();
}
下面来看看同步注册表,同样在EurekaBootStrap中
Applications apps = eurekaClient.getApplications();
eureka server其实自己也是个eureka client,client在初始化的时候,会从相邻的eureka server中拉取注册表到本地,也作为eureka server的本地注册表
// Copy registry from neighboring eureka node
//从相邻eureka-server节点同步注册信息
int registryCount = registry.syncUp();
@Override
public int syncUp() {
// Copy entire entry from neighboring DS node
int count = 0;
for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) {
if (i > 0) {
try {
Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs());
} catch (InterruptedException e) {
logger.warn("Interrupted during registry transfer..");
break;
}
}
Applications apps = eurekaClient.getApplications();
for (Application app : apps.getRegisteredApplications()) {
for (InstanceInfo instance : app.getInstances()) {
try {
if (isRegisterable(instance)) {
register(instance, instance.getLeaseInfo().getDurationInSecs(), true);
count++;
}
} catch (Throwable t) {
logger.error("During DS init copy", t);
}
}
}
}
return count;
}
//重试5次
@Override
public int getRegistrySyncRetries() {
return configInstance.getIntProperty(
namespace + "numberRegistrySyncRetries", 5).get();
}
下面再来看看 注册、下线、故障 这些集群是怎么同步的?
注册:
replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication); 这方法就是将注册同步给其他eureka server
@Override
public void register(final InstanceInfo info, final boolean isReplication) {
int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
super.register(info, leaseDuration, isReplication);
replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}
private void replicateInstanceActionsToPeers(Action action, String appName,
String id, InstanceInfo info, InstanceStatus newStatus,
PeerEurekaNode node) {
try {
InstanceInfo infoFromRegistry;
CurrentRequestVersion.set(Version.V2);
switch (action) {
case Cancel:
node.cancel(appName, id);
break;
case Heartbeat:
InstanceStatus overriddenStatus = overriddenInstanceStatusMap.get(id);
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.heartbeat(appName, id, infoFromRegistry, overriddenStatus, false);
break;
case Register:
node.register(info);
break;
case StatusUpdate:
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.statusUpdate(appName, id, newStatus, infoFromRegistry);
break;
case DeleteStatusOverride:
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.deleteStatusOverride(appName, id, infoFromRegistry);
break;
}
} catch (Throwable t) {
logger.error("Cannot replicate information to {} for action {}", node.getServiceUrl(), action.name(), t);
} finally {
CurrentRequestVersion.remove();
}
}
就来看看注册这块吧
1.batchingDispatcher.process()
2.AcceptorExecutor.process()中acceptorQueue.add()
3.AcceptorExecutor中的AcceptorRunner会将中acceptorQueue中的元素放入processingOrder中,AcceptorExecutor会每隔500ms将processingOrder中元素(最多250个)打包到batchWorkQueue中
4.ReplicationTaskProcessor会调用replicationClient.submitBatchUpdates(list);通过一次网络请求将batch发送到其他eureka server
public void register(final InstanceInfo info) throws Exception {
long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);
batchingDispatcher.process(
taskId("register", info),
new InstanceReplicationTask(targetHost, Action.Register, info, null, true) {
public EurekaHttpResponse<Void> execute() {
return replicationClient.register(info);
}
},
expiryTime
);
}
总结:
采用三个队列,异步批量同步