目录
一、Eureka同步逻辑
1. EurekaHttpClient从Client列表中随机选择
2. NamingService从Client列表中随机选择
3. EurekaHttpResponse中InstanceInfo同步
4. 校验HTTP状态码
5. registerInstance()同步服务注册
6. 同步任务订阅到SpecialSyncEventBus
二、zk同步逻辑
1. 根据serviceName的path和随机Client得到PathChildrenCache
2. NamingService从Client列表中随机选择
3. 获取当前数据List
4. 校验queryParam
5. registerInstance()同步服务注册
6. 添加监听器到listener
根据Guava的EventBus模式进行不同注册中心的服务同步(迁移)。
通过EventListener触发同步
public class EventListener {
@Subscribe
public void listenerSyncTaskEvent(SyncTaskEvent syncTaskEvent) {
try {
long start = System.currentTimeMillis();
syncManagerService.sync(syncTaskEvent.getTaskDO());
skyWalkerCacheServices.addFinishedTask(syncTaskEvent.getTaskDO());
metricsManager.record(MetricsStatisticsType.SYNC_TASK_RT, System.currentTimeMillis() - start);
} catch (Exception e) {
log.warn("listenerSyncTaskEvent process error", e);
}
}
}
public class SyncManagerService implements InitializingBean, ApplicationContextAware {
public boolean sync(TaskDO taskDO) {
return getSyncService(taskDO.getSourceClusterId(), taskDO.getDestClusterId()).sync(taskDO);
}
public SyncService getSyncService(String sourceClusterId, String destClusterId) {
ClusterTypeEnum sourceClusterType = this.skyWalkerCacheServices.getClusterType(sourceClusterId);
ClusterTypeEnum destClusterType = this.skyWalkerCacheServices.getClusterType(destClusterId);
return syncServiceMap.get(generateSyncKey(sourceClusterType, destClusterType));
}
}
SyncService的实现类sync()同步实现逻辑:
@NacosSyncService(sourceCluster = ClusterTypeEnum.EUREKA, destinationCluster = ClusterTypeEnum.NACOS)
public class EurekaSyncToNacosServiceImpl implements SyncService {
@Override
public boolean sync(TaskDO taskDO) {
try {
//random.nextInt(allClusterConnectKey.size())从client列表中随机选择一个client
//1.EurekaHttpClient
EurekaHttpClient eurekaHttpClient = eurekaServerHolder.get(taskDO.getSourceClusterId(), null);
//2.NamingService
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId(), null);
//3.EurekaHttpResponse
EurekaHttpResponse eurekaHttpResponse =
eurekaHttpClient.getApplication(taskDO.getServiceName());
//Whether eurekaHttpResponse.getStatusCode() is in the HTTP series
//4.HTTP状态码校验
if (Objects.requireNonNull(HttpStatus.resolve(eurekaHttpResponse.getStatusCode())).is2xxSuccessful()) {
Application application = eurekaHttpResponse.getEntity();
for (InstanceInfo instanceInfo : application.getInstances()) {
//5.同步
if (needSync(instanceInfo.getMetadata())) {
if (InstanceInfo.InstanceStatus.UP.equals(instanceInfo.getStatus())) {
destNamingService.registerInstance(taskDO.getServiceName(),
buildSyncInstance(instanceInfo, taskDO));
} else {
destNamingService.deregisterInstance(instanceInfo.getAppName(), instanceInfo.getIPAddr(),
instanceInfo.getPort());
}
}
}
} else {
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
throw new RuntimeException("trying to connect to the server failed");
}
//6.订阅到SpecialSyncEventBus
specialSyncEventBus.subscribe(taskDO, this::sync);
} catch (Exception e) {
log.error("sync task from eureka to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
}
EurekaHttpClient eurekaHttpClient = eurekaServerHolder.get(taskDO.getSourceClusterId(), null);
public abstract class AbstractServerHolder implements Holder {
@Override
public T get(String clusterId, String namespace) {
//namespace=null,则finalNamespace=" ";否则为namespace
final String finalNamespace = Optional.ofNullable(namespace).orElse("");
//key=clusterId_finalNamespace
String key = Joiner.on("_").join(clusterId,finalNamespace);
log.info("starting create cluster server,clusterId={}", clusterId);
//if key 不存在于serviceMap,则调用传入的function计算key的value,放入缓存map中
serviceMap.computeIfAbsent(key, clusterKey -> {
try {
//随机从random.nextInt(allClusterConnectKey.size())中选择Cluster
return createServer(clusterId,
() -> skyWalkerCacheServices.getClusterConnectKey(clusterId),
finalNamespace);
} catch (Exception e) {
log.error(String.format("clusterId=%s,start server failed", clusterId), e);
return null;
}
});
return serviceMap.get(key);
}
}
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId(), null);
InstanceInfo存储服务信息。
EurekaHttpResponse eurekaHttpResponse =
eurekaHttpClient.getApplication(taskDO.getServiceName());
if (InstanceInfo.InstanceStatus.UP.equals(instanceInfo.getStatus()))
destNamingService.registerInstance(taskDO.getServiceName(),
buildSyncInstance(instanceInfo, taskDO));
specialSyncEventBus.subscribe(taskDO, this::sync);
@NacosSyncService(sourceCluster = ClusterTypeEnum.ZK, destinationCluster = ClusterTypeEnum.NACOS)
public class ZookeeperSyncToNacosServiceImpl implements SyncService {
@Override
public boolean sync(TaskDO taskDO) {
try {
if (pathChildrenCacheMap.containsKey(taskDO.getTaskId())) {
return true;
}
//使用PathChildrenCache监听zk集群当前目录+子节点。
//1.PathChildrenCache
PathChildrenCache pathChildrenCache = getPathCache(taskDO);
//2.NamingService
NamingService destNamingService = nacosServerHolder
.get(taskDO.getDestClusterId(), null);
//3.List
//获取当前数据:childData存储路径String path、信息Stat stat
List currentData = pathChildrenCache.getCurrentData();
for (ChildData childData : currentData) {
String path = childData.getPath();
Map queryParam = parseQueryString(childData.getPath());
//4.queryParam校验
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map ipAndPortParam = parseIpAndPortString(path);
Instance instance = buildSyncInstance(queryParam, ipAndPortParam, taskDO);
//5.同步
destNamingService.registerInstance(
getServiceNameFromCache(taskDO.getTaskId(), queryParam),
instance);
}
}
//6.添加监听器
Objects.requireNonNull(pathChildrenCache).getListenable()
.addListener((client, event) -> {
try {
String path = event.getData().getPath();
Map queryParam = parseQueryString(path);
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map ipAndPortParam = parseIpAndPortString(path);
Instance instance = buildSyncInstance(queryParam, ipAndPortParam,
taskDO);
switch (event.getType()) {
case CHILD_ADDED:
destNamingService.registerInstance(
getServiceNameFromCache(taskDO.getTaskId(),
queryParam), instance);
break;
case CHILD_UPDATED:
destNamingService.registerInstance(
getServiceNameFromCache(taskDO.getTaskId(),
queryParam), instance);
break;
case CHILD_REMOVED:
destNamingService.deregisterInstance(
getServiceNameFromCache(taskDO.getTaskId(),
queryParam),
ipAndPortParam.get(INSTANCE_IP_KEY),
Integer.parseInt(
ipAndPortParam.get(INSTANCE_PORT_KEY)));
break;
default:
break;
}
}
} catch (Exception e) {
log.error("event process from zookeeper to nacos was failed, taskId:{}",
taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
}
});
} catch (Exception e) {
log.error("sync task from zookeeper to nacos was failed, taskId:{}", taskDO.getTaskId(),
e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
}
PathChildrenCache监听zk集群中节点信息。
PathChildrenCache pathChildrenCache = getPathCache(taskDO);
/**
* fetch the Path cache when the task sync
*/
protected PathChildrenCache getPathCache(TaskDO taskDO) {
return pathChildrenCacheMap.computeIfAbsent(taskDO.getTaskId(), (key) -> {
try {
//集群中每个节点启动时,都注册到ZKServer上,然后每个节点都接收节点改变的监听,以便每个节点都实时更新集群信息(其他应用要使用)
//监听使用PathChildrenCache,能监听当前目录+子节点。
PathChildrenCache pathChildrenCache =
new PathChildrenCache(
//从client列表中随机得到一个client(CuratorFramework的client)
zookeeperServerHolder.get(taskDO.getSourceClusterId(), ""),
//路径:/dubbo/serviceName/providers
convertDubboProvidersPath(taskDO.getServiceName()), false);
//BUILD_INITIAL_CACHE模式启动cache
// 调用rebuild方法(此方法会阻塞执行)
// 重新查询所有需要的数据
// 不会触发任何事件
// 安全创建path
// 清空currentData缓存
// 重新加载path下子节点,逐个结点重构缓存
// 逐个读取节点数据和状态
// 构建ChildData放入currentData
// 通过rebuildTestExchanger发送要给信号对象
pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
return pathChildrenCache;
} catch (Exception e) {
log.error("zookeeper path children cache start failed, taskId:{}",
taskDO.getTaskId(), e);
return null;
}
});
}
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId(), null);
List currentData = pathChildrenCache.getCurrentData();
ChildData存储path、服务信息Stat。
public class ChildData implements Comparable
{
private final String path;
private final Stat stat;
private final byte[] data;
}
Map queryParam = parseQueryString(childData.getPath());
if (isMatch(taskDO, queryParam) && needSync(queryParam))
destNamingService.registerInstance(
getServiceNameFromCache(taskDO.getTaskId(), queryParam),
instance);
Objects.requireNonNull(pathChildrenCache).getListenable()
.addListener((client, event))
参考:https://my.oschina.net/roccn/blog/918209