目录
1. EurekaHttpClient从Client列表中随机选择
2. NamingService从Client列表中随机选择
3. EurekaHttpResponse中InstanceInfo同步
1. 根据serviceName的path和随机Client得到PathChildrenCache
2. NamingService从Client列表中随机选择
根据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()同步实现逻辑:
一、Eureka同步逻辑
- EurekaHttpClient
- NamingService
- EurekaHttpResponse
- HTTP状态码校验
- 同步
- 订阅到SpecialSyncEventBus
@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<Application> 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;
}
}
1. EurekaHttpClient从Client列表中随机选择
EurekaHttpClient eurekaHttpClient = eurekaServerHolder.get(taskDO.getSourceClusterId(), null);
public abstract class AbstractServerHolder<T> 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);
}
}
2. NamingService从Client列表中随机选择
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId(), null);
3. EurekaHttpResponse中InstanceInfo同步
InstanceInfo存储服务信息。
EurekaHttpResponse<Application> eurekaHttpResponse =
eurekaHttpClient.getApplication(taskDO.getServiceName());
4. 校验HTTP状态码
if (InstanceInfo.InstanceStatus.UP.equals(instanceInfo.getStatus()))
5. registerInstance()同步服务注册
destNamingService.registerInstance(taskDO.getServiceName(),
buildSyncInstance(instanceInfo, taskDO));
6. 同步任务订阅到SpecialSyncEventBus
specialSyncEventBus.subscribe(taskDO, this::sync);
二、zk同步逻辑
- PathChildrenCache
- NamingService
- List<ChildData>
- queryParam校验
- 同步
- 添加监听器
@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>
//获取当前数据:childData存储路径String path、信息Stat stat
List<ChildData> currentData = pathChildrenCache.getCurrentData();
for (ChildData childData : currentData) {
String path = childData.getPath();
Map<String, String> queryParam = parseQueryString(childData.getPath());
//4.queryParam校验
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map<String, String> 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<String, String> queryParam = parseQueryString(path);
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map<String, String> 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;
}
}
1. 根据serviceName的path和随机Client得到PathChildrenCache
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;
}
});
}
2. NamingService从Client列表中随机选择
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId(), null);
3. 获取当前数据List<ChildData>
List<ChildData> currentData = pathChildrenCache.getCurrentData();
ChildData存储path、服务信息Stat。
public class ChildData implements Comparable<ChildData>
{
private final String path;
private final Stat stat;
private final byte[] data;
}
4. 校验queryParam
Map<String, String> queryParam = parseQueryString(childData.getPath());
if (isMatch(taskDO, queryParam) && needSync(queryParam))
5. registerInstance()同步服务注册
destNamingService.registerInstance(
getServiceNameFromCache(taskDO.getTaskId(), queryParam),
instance);
6. 添加监听器到listener
Objects.requireNonNull(pathChildrenCache).getListenable()
.addListener((client, event))