NacosSync

目录

一、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()同步实现逻辑:

一、Eureka同步逻辑

  1. EurekaHttpClient
  2. NamingService
  3. EurekaHttpResponse
  4. HTTP状态码校验
  5. 同步
  6. 订阅到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同步逻辑

  1. PathChildrenCache
  2. NamingService
  3. List<ChildData>
  4. queryParam校验
  5. 同步
  6. 添加监听器
@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))

 

参考:https://my.oschina.net/roccn/blog/918209

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kubernetes***开源的容器编排平台,而NacosSync是一个用于同步Nacos配置中心数据到本地文件的工具。下面是安装NacosSync的步骤: 1. 首先,确保你已经安装了Kubernetes集群,并且集群正常运行。 2. 在Kubernetes集群中创建一个命名空间(Namespace),用于部署NacosSync。可以使用以下命令创建一个名为nacos-sync的命名空间: ``` kubectl create namespace nacos-sync ``` 3. 创建一个用于部署NacosSync的配置文件,例如nacos-sync.yaml,并将以下内容添加到文件中: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nacos-sync namespace: nacos-sync spec: replicas: 1 selector: matchLabels: app: nacos-sync template: metadata: labels: app: nacos-sync spec: containers: - name: nacos-sync image: <nacos-sync-image> env: - name: NACOS_SERVER_ADDR value: <nacos-server-addr> - name: NACOS_NAMESPACE value: <nacos-namespace> - name: NACOS_DATA_ID value: <nacos-data-id> - name: LOCAL_FILE_PATH value: <local-file-path> ``` 其中,`<nacos-sync-image>`是NacosSync的镜像地址,`<nacos-server-addr>`是Nacos服务器的地址,`<nacos-namespace>`是Nacos的命名空间,`<nacos-data-id>`是要同步的Nacos配置数据ID,`<local-file-path>`是本地文件路径。 4. 使用kubectl命令部署NacosSync: ``` kubectl apply -f nacos-sync.yaml ``` 5. 等待部署完成后,可以使用以下命令检查NacosSync的运行状态: ``` kubectl get pods -n nacos-sync ``` 至此,你已经成功安装了NacosSync,并且配置了要同步的Nacos配置数据和本地文件路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值