diff --git a/README.md b/README.md
index 013bdf2a..97dc7c19 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,7 @@ The path to the target file:
``` xml
-nacos-sync/nacossync-distribution/target/nacos-sync-0.4.8.tar.gz
+nacos-sync/nacossync-distribution/target/nacos-sync-0.4.9-batchRegister.tar.gz
```
diff --git a/nacossync-console/src/main/resources/static/console-fe/src/containers/Layout/Layout.js b/nacossync-console/src/main/resources/static/console-fe/src/containers/Layout/Layout.js
index f0a87dd3..6842a7d8 100644
--- a/nacossync-console/src/main/resources/static/console-fe/src/containers/Layout/Layout.js
+++ b/nacossync-console/src/main/resources/static/console-fe/src/containers/Layout/Layout.js
@@ -13,7 +13,7 @@ class Layout extends React.Component {
- Nacos-Sync 0.4.8
+ Nacos-Sync 0.4.9-batchRegister
{this.props.children}
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/ClusterAccessService.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/ClusterAccessService.java
index 2e11ca33..1795fdce 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/ClusterAccessService.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/ClusterAccessService.java
@@ -107,7 +107,12 @@ private List getPredicates(Root root, CriteriaBuilder crit
public int findClusterLevel(String sourceClusterId){
ClusterDO clusterDO = clusterRepository.findByClusterId(sourceClusterId);
if (clusterDO != null) {
- return clusterDO.getClusterLevel();
+ Integer level = clusterDO.getClusterLevel();
+ if (level == null) {
+ //此字段未设置时取默认0按普通集群处理(目前控制台新增集群的代码并未设置此字段)
+ return 0;
+ }
+ return level;
}
return -1;
}
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/NacosServerHolder.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/NacosServerHolder.java
index dda88a53..9047401d 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/NacosServerHolder.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/NacosServerHolder.java
@@ -13,15 +13,21 @@
package com.alibaba.nacossync.extension.holder;
import com.alibaba.nacos.api.PropertyKeyConst;
+import com.alibaba.nacos.api.config.ConfigFactory;
+import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
+import com.alibaba.nacos.client.naming.NacosNamingService;
+import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate;
+import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.dao.ClusterAccessService;
import com.alibaba.nacossync.dao.TaskAccessService;
import com.alibaba.nacossync.pojo.model.ClusterDO;
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.google.common.base.Joiner;
+import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
@@ -29,8 +35,8 @@
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.util.ReflectionUtils;
/**
* @author paderlol
@@ -44,7 +50,11 @@ public class NacosServerHolder extends AbstractServerHolderImpl {
private final TaskAccessService taskAccessService;
- private static ConcurrentHashMap globalNameService = new ConcurrentHashMap<>(16);
+ private static ConcurrentHashMap globalNamingService = new ConcurrentHashMap<>(16);
+
+ private static ConcurrentHashMap globalNamingHttpProxy = new ConcurrentHashMap<>(16);
+
+ private static ConcurrentHashMap globalConfigService = new ConcurrentHashMap<>(16);
public NacosServerHolder(ClusterAccessService clusterAccessService, TaskAccessService taskAccessService) {
this.clusterAccessService = clusterAccessService;
@@ -68,8 +78,6 @@ NamingService createServer(String clusterId, Supplier serverAddressSuppl
String serverList = Joiner.on(",").join(allClusterConnectKey);
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
- properties.setProperty(PropertyKeyConst.NAMESPACE, Optional.ofNullable(clusterDO.getNamespace()).orElse(
- Strings.EMPTY));
Optional.ofNullable(clusterDO.getUserName()).ifPresent(value ->
properties.setProperty(PropertyKeyConst.USERNAME, value)
);
@@ -77,9 +85,42 @@ NamingService createServer(String clusterId, Supplier serverAddressSuppl
Optional.ofNullable(clusterDO.getPassword()).ifPresent(value ->
properties.setProperty(PropertyKeyConst.PASSWORD, value)
);
- NamingService namingService = NamingFactory.createNamingService(properties);
- globalNameService.put(clusterId,namingService);
- return namingService;
+
+ // configService不能设置namespace,否则获取不到dubbo服务元数据
+ globalConfigService.computeIfAbsent(newClusterId, id -> {
+ try {
+ return ConfigFactory.createConfigService(properties);
+ } catch (NacosException e) {
+ log.error("start config service fail,clusterId:{}", id, e);
+ return null;
+ }
+ });
+
+ properties.setProperty(PropertyKeyConst.NAMESPACE, Optional.ofNullable(clusterDO.getNamespace()).orElse(
+ Strings.EMPTY));
+ return globalNamingService.computeIfAbsent(newClusterId, id -> {
+ try {
+ NacosNamingService namingService = (NacosNamingService) NamingFactory.createNamingService(properties);
+
+ // clientProxy
+ final Field clientProxyField = ReflectionUtils.findField(NacosNamingService.class, "clientProxy");
+ assert clientProxyField != null;
+ ReflectionUtils.makeAccessible(clientProxyField);
+ NamingClientProxyDelegate clientProxy = (NamingClientProxyDelegate) ReflectionUtils.getField(clientProxyField, namingService);
+
+ // httpClientProxy
+ final Field httpClientProxyField = ReflectionUtils.findField(NamingClientProxyDelegate.class, "httpClientProxy");
+ assert httpClientProxyField != null;
+ ReflectionUtils.makeAccessible(httpClientProxyField);
+ NamingHttpClientProxy httpClientProxy = (NamingHttpClientProxy) ReflectionUtils.getField(httpClientProxyField, clientProxy);
+ globalNamingHttpProxy.put(id, httpClientProxy);
+
+ return namingService;
+ } catch (NacosException e) {
+ log.error("start naming service fail,clusterId:{}", id, e);
+ return null;
+ }
+ });
}
/**
@@ -87,10 +128,18 @@ NamingService createServer(String clusterId, Supplier serverAddressSuppl
* @param clusterId clusterId
* @return Returns Naming Service objects for different clusters
*/
- public NamingService getNameService(String clusterId){
- return globalNameService.get(clusterId);
+ public NamingService getNamingService(String clusterId){
+ return globalNamingService.get(clusterId);
}
-
+
+ public NamingHttpClientProxy getNamingHttpProxy(String clusterId){
+ return globalNamingHttpProxy.get(clusterId);
+ }
+
+ public ConfigService getConfigService(String clusterId) {
+ return globalConfigService.get(clusterId);
+ }
+
public NamingService getSourceNamingService(String taskId, String sourceClusterId) {
String key = taskId + sourceClusterId;
return serviceMap.computeIfAbsent(key, k->{
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ConsulSyncToNacosServiceImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ConsulSyncToNacosServiceImpl.java
index 1f8e3b59..b3d367df 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ConsulSyncToNacosServiceImpl.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ConsulSyncToNacosServiceImpl.java
@@ -15,6 +15,7 @@
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
+import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
@@ -32,6 +33,8 @@
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.health.model.HealthService;
+
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -77,16 +80,20 @@ public boolean delete(TaskDO taskDO) {
specialSyncEventBus.unsubscribe(taskDO);
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
- List allInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()));
+ String servideName = taskDO.getServiceName();
+ String groupName = NacosUtils.getGroupNameOrDefault(taskDO.getGroupName());
+ List allInstances = destNamingService.getAllInstances(servideName, groupName);
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)) {
-
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
-
+ if (CollectionUtils.isNotEmpty(needDeregisterInstances)) {
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, servideName, groupName, needDeregisterInstances);
+ }
} catch (Exception e) {
log.error("delete task from consul to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR);
@@ -117,26 +124,47 @@ public boolean sync(TaskDO taskDO, Integer index) {
private void cleanAllOldInstance(TaskDO taskDO, NamingService destNamingService, Set instanceKeys)
throws NacosException {
- List allInstances = destNamingService.getAllInstances(taskDO.getServiceName());
+ String serviceName = taskDO.getServiceName();
+ String groupName = NacosUtils.getGroupNameOrDefault(taskDO.getGroupName());
+ List allInstances = destNamingService.getAllInstances(serviceName, groupName);
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)
&& !instanceKeys.contains(composeInstanceKey(instance.getIp(), instance.getPort()))) {
-
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
+ if (CollectionUtils.isNotEmpty(needDeregisterInstances)) {
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, serviceName, groupName, needDeregisterInstances);
+ }
}
private void overrideAllInstance(TaskDO taskDO, NamingService destNamingService,
List healthServiceList, Set instanceKeys) throws NacosException {
+ String serviceName = taskDO.getServiceName();
+ String groupName = NacosUtils.getGroupNameOrDefault(taskDO.getGroupName());
+ List needRegisterInstances = new ArrayList<>();
for (HealthService healthService : healthServiceList) {
if (needSync(ConsulUtils.transferMetadata(healthService.getService().getTags()))) {
- destNamingService.registerInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()),
- buildSyncInstance(healthService, taskDO));
+ Instance syncInstance = buildSyncInstance(healthService, taskDO);
+ log.debug("需要从源集群同步到目标集群的实例:{}", syncInstance);
+ needRegisterInstances.add(syncInstance);
instanceKeys.add(composeInstanceKey(healthService.getService().getAddress(),
- healthService.getService().getPort()));
+ healthService.getService().getPort()));
+ }
+ }
+ if (CollectionUtils.isNotEmpty(needRegisterInstances)) {
+ if (needRegisterInstances.get(0).isEphemeral()) {
+ //批量注册
+ log.debug("将源集群指定service的临时实例全量同步到目标集群: {}", taskDO);
+ destNamingService.batchRegisterInstance(serviceName, groupName, needRegisterInstances);
+ } else {
+ for (Instance instance : needRegisterInstances) {
+ log.debug("从源集群同步到目标集群的持久实例:{}", instance);
+ destNamingService.registerInstance(serviceName, groupName, instance);
+ }
}
}
}
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/EurekaSyncToNacosServiceImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/EurekaSyncToNacosServiceImpl.java
index e2b4c634..b098bb72 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/EurekaSyncToNacosServiceImpl.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/EurekaSyncToNacosServiceImpl.java
@@ -29,6 +29,8 @@
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.alibaba.nacossync.util.NacosUtils;
import com.netflix.appinfo.InstanceInfo;
+
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -118,13 +120,29 @@ public boolean sync(TaskDO taskDO,Integer index) {
private void addValidInstance(TaskDO taskDO, NamingService destNamingService, List eurekaInstances)
throws NacosException {
+ String serviceName = taskDO.getServiceName();
+ String groupName = NacosUtils.getGroupNameOrDefault(taskDO.getGroupName());
+ List needRegisterInstances = new ArrayList<>();
for (InstanceInfo instance : eurekaInstances) {
if (needSync(instance.getMetadata())) {
log.info("Add service instance from Eureka, serviceName={}, Ip={}, port={}",
- instance.getAppName(), instance.getIPAddr(), instance.getPort());
- destNamingService.registerInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), buildSyncInstance(instance,
- taskDO));
+ instance.getAppName(), instance.getIPAddr(), instance.getPort());
+ Instance syncInstance = buildSyncInstance(instance, taskDO);
+ log.debug("需要从源集群同步到目标集群的实例:{}", syncInstance);
+ needRegisterInstances.add(syncInstance);
+ }
+ }
+ if (CollectionUtils.isEmpty(needRegisterInstances)) {
+ return;
+ }
+ if (needRegisterInstances.get(0).isEphemeral()) {
+ // 批量注册
+ log.debug("将源集群指定service的临时实例全量同步到目标集群: {}", taskDO);
+ destNamingService.batchRegisterInstance(serviceName, groupName, needRegisterInstances);
+ } else {
+ for (Instance instance : needRegisterInstances) {
+ log.debug("从源集群同步到目标集群的持久实例:{}", instance);
+ destNamingService.registerInstance(serviceName, groupName, instance);
}
}
}
@@ -135,26 +153,40 @@ private void deleteAllInstanceFromEureka(TaskDO taskDO, NamingService destNaming
if (CollectionUtils.isEmpty(eurekaInstances)) {
return;
}
+ List needDeregisterInstances = new ArrayList<>();
for (InstanceInfo instance : eurekaInstances) {
if (needSync(instance.getMetadata())) {
log.info("Delete service instance from Eureka, serviceName={}, Ip={}, port={}",
instance.getAppName(), instance.getIPAddr(), instance.getPort());
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), buildSyncInstance(instance, taskDO));
+ Instance needDeregisterInstance = buildSyncInstance(instance, taskDO);
+ log.debug("需要反注册的实例: {}", needDeregisterInstance);
+ needDeregisterInstances.add(needDeregisterInstance);
}
}
+ if (CollectionUtils.isEmpty(needDeregisterInstances)) {
+ return;
+ }
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, taskDO.getServiceName(),
+ NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), needDeregisterInstances);
}
private void removeInvalidInstance(TaskDO taskDO, NamingService destNamingService,
List eurekaInstances, List nacosInstances) throws NacosException {
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : nacosInstances) {
if (!isExistInEurekaInstance(eurekaInstances, instance) && needDelete(instance.getMetadata(), taskDO)) {
log.info("Remove invalid service instance from Nacos, serviceName={}, Ip={}, port={}",
instance.getServiceName(), instance.getIp(), instance.getPort());
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
+ if (CollectionUtils.isEmpty(needDeregisterInstances)) {
+ return;
+ }
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, taskDO.getServiceName(),
+ NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), needDeregisterInstances);
}
private boolean isExistInEurekaInstance(List eurekaInstances, Instance nacosInstance) {
@@ -165,20 +197,26 @@ private boolean isExistInEurekaInstance(List eurekaInstances, Inst
private void deleteAllInstance(TaskDO taskDO, NamingService destNamingService, List allInstances)
throws NacosException {
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)) {
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance);
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
-
}
+ if (CollectionUtils.isEmpty(needDeregisterInstances)) {
+ return;
+ }
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, taskDO.getServiceName(),
+ NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), needDeregisterInstances);
}
private Instance buildSyncInstance(InstanceInfo instance, TaskDO taskDO) {
Instance temp = new Instance();
temp.setIp(instance.getIPAddr());
temp.setPort(instance.getPort());
- temp.setServiceName(instance.getAppName());
+ //查询nacos集群实例返回的serviceName含组名前缀,但Nacos2服务端检查批量注册请求serviceName参数时不能包含组名前缀,因此注册实例到目标集群时不再设置serviceName。
temp.setHealthy(true);
Map metaData = new HashMap<>(instance.getMetadata());
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToNacosServiceImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToNacosServiceImpl.java
index b72759b4..2c956f0d 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToNacosServiceImpl.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToNacosServiceImpl.java
@@ -13,14 +13,21 @@
package com.alibaba.nacossync.extension.impl;
+import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
+import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.ConcurrentHashSet;
+import com.alibaba.nacos.common.utils.JacksonUtils;
+import com.alibaba.nacos.common.utils.MD5Utils;
+import com.alibaba.nacos.shaded.com.google.gson.Gson;
+import com.alibaba.nacos.shaded.com.google.gson.JsonSyntaxException;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
@@ -33,20 +40,29 @@
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.alibaba.nacossync.template.processor.TaskUpdateProcessor;
import com.alibaba.nacossync.timer.FastSyncHelper;
+import com.alibaba.nacossync.util.DubboConstants;
import com.alibaba.nacossync.util.StringUtils;
+import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
-import java.util.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-
-import static com.alibaba.nacossync.constant.SkyWalkerConstants.SOURCE_CLUSTERID_KEY;
+import javax.ws.rs.HttpMethod;
import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault;
/**
@@ -58,6 +74,32 @@
@NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.NACOS)
public class NacosSyncToNacosServiceImpl implements SyncService, InitializingBean {
+ private static final Gson GSON = new Gson();
+
+ private static final long GET_CFG_TIMEOUT = 3000L;
+
+ private static final int METADATA_CACHE_LIVE_MS = 180_000;
+
+ private static final Map MAPPING_METADATA_CACHE = new ConcurrentHashMap<>(16);
+
+ private static final String GET_MAPPING_CFG_URL = "/nacos/v1/cs/configs";
+
+ private static final Map GET_MAPPING_CFG_BASE_PARAMS = new HashMap<>(8);
+ static {
+ //采用精确模式检索
+ GET_MAPPING_CFG_BASE_PARAMS.put("search", "accurate");
+ //nacos服务端要求查询必须包含dataId条件
+ GET_MAPPING_CFG_BASE_PARAMS.put("dataId", "");
+ //查询group类型为映射关系的配置数据
+ GET_MAPPING_CFG_BASE_PARAMS.put("group", "mapping");
+ }
+
+ private static final String PAGE_NO_KEY = "pageNo";
+
+ private static final String PAGE_SIZE_KEY = "pageSize";
+
+ private static final int PAGE_SIZE = 100;
+
private Map listenerMap = new ConcurrentHashMap<>();
private final Map syncTaskTap = new ConcurrentHashMap<>();
@@ -104,6 +146,9 @@ public void afterPropertiesSet() {
}
try {
+ //清除接口应用名映射关系元数据缓存
+ MAPPING_METADATA_CACHE.clear();
+
Collection taskCollections = allSyncTaskMap.values();
List taskDOList = new ArrayList<>(taskCollections);
@@ -120,70 +165,83 @@ public void afterPropertiesSet() {
@Override
public boolean delete(TaskDO taskDO) {
try {
- NamingService sourceNamingService = nacosServerHolder.getSourceNamingService(taskDO.getTaskId(),
+ String taskId = taskDO.getTaskId();
+ NamingService sourceNamingService = nacosServerHolder.getSourceNamingService(taskId,
taskDO.getSourceClusterId());
+ String groupName = getGroupNameOrDefault(taskDO.getGroupName());
+
int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId());
if ("ALL".equals(taskDO.getServiceName())) {
- String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskDO.getTaskId());
+ String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskId);
if (!StringUtils.isEmpty(operationId)) {
allSyncTaskMap.remove(operationId);
}
//处理group级别的服务任务删除
- ListView servicesOfServer = sourceNamingService.getServicesOfServer(0, Integer.MAX_VALUE,
- taskDO.getGroupName());
+ ListView servicesOfServer = sourceNamingService.getServicesOfServer(
+ 0, Integer.MAX_VALUE, groupName);
List serviceNames = servicesOfServer.getData();
for (String serviceName : serviceNames) {
- String operationKey = taskDO.getTaskId() + serviceName;
+ String operationKey = taskId + serviceName;
skyWalkerCacheServices.removeFinishedTask(operationKey);
allSyncTaskMap.remove(operationKey);
+ sourceNamingService.unsubscribe(serviceName, groupName, listenerMap.remove(operationKey));
+
+ //popNamingService方法需要传入serviceName属性为实际服务名的TaskDO对象
TaskDO task = new TaskDO();
BeanUtils.copyProperties(taskDO, task);
task.setServiceName(serviceName);
- task.setOperationId(taskDO.getTaskId() + serviceName);
+ task.setOperationId(operationKey);
NamingService destNamingService = popNamingService(task);
- sourceNamingService.unsubscribe(serviceName, getGroupNameOrDefault(task.getGroupName()),
- listenerMap.remove(task.getTaskId() + serviceName));
if (Objects.isNull(destNamingService)) {
- log.warn("task {} not found NamingService", task.getTaskId() + serviceName);
+ log.warn("task {} not found NamingService", operationKey);
continue;
}
- List sourceInstances = sourceNamingService.getAllInstances(serviceName,
- getGroupNameOrDefault(task.getGroupName()), new ArrayList<>(), false);
+
+ List sourceInstances = sourceNamingService.getAllInstances(
+ serviceName, groupName, new ArrayList<>(), false);
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : sourceInstances) {
-// if (needSync(instance.getMetadata())) {
-// destNamingService.deregisterInstance(serviceName,
-// getGroupNameOrDefault(task.getGroupName()), instance.getIp(), instance.getPort());
-// }
- if (needSync(instance.getMetadata(),level , taskDO.getDestClusterId())){
- destNamingService.deregisterInstance(serviceName,
- getGroupNameOrDefault(task.getGroupName()), instance.getIp(), instance.getPort());
+ if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) {
+ removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
+ if (CollectionUtils.isNotEmpty(needDeregisterInstances)) {
+ doDeregisterInstance(taskDO, destNamingService,
+ serviceName, groupName, needDeregisterInstances);
+ }
}
} else {
//处理服务级别的任务删除
- String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskDO.getTaskId());
+ String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskId);
if (StringUtils.isEmpty(operationId)) {
log.warn("operationId is null data synchronization is not currently performed.{}", operationId);
return false;
}
- sourceNamingService.unsubscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
- listenerMap.remove(operationId));
- List sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), false);
-
+ String serviceName = taskDO.getServiceName();
+ sourceNamingService.unsubscribe(serviceName, groupName, listenerMap.remove(operationId));
+
NamingService destNamingService = popNamingService(taskDO);
+ if (Objects.isNull(destNamingService)) {
+ log.warn("task {} not found NamingService", operationId);
+ return false;
+ }
+
+ List sourceInstances = sourceNamingService.getAllInstances(
+ serviceName, groupName, new ArrayList<>(), false);
+ List needDeregisterInstances = new ArrayList<>();
for (Instance instance : sourceInstances) {
- if (needSync(instance.getMetadata(),level , taskDO.getDestClusterId())){
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), instance);
+ if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) {
+ removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
-// if (needSync(instance.getMetadata())) {
-// destNamingService.deregisterInstance(taskDO.getServiceName(),
-// getGroupNameOrDefault(taskDO.getGroupName()), instance);
-// }
+ }
+ if (CollectionUtils.isNotEmpty(needDeregisterInstances)) {
+ doDeregisterInstance(taskDO, destNamingService, serviceName, groupName, needDeregisterInstances);
}
// 移除任务
skyWalkerCacheServices.removeFinishedTask(operationId);
@@ -271,72 +329,121 @@ private void doSync(String taskId, TaskDO taskDO, NamingService sourceNamingServ
recordNamingService(taskDO, destNamingService);
try {
- List sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
+ String serviceName = taskDO.getServiceName();
+ String groupName = getGroupNameOrDefault(taskDO.getGroupName());
+ List sourceInstances = sourceNamingService.getAllInstances(serviceName,
+ groupName, new ArrayList<>(), true);
- int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId());
+ String sourceClusterId = taskDO.getSourceClusterId();
+ int level = clusterAccessService.findClusterLevel(sourceClusterId);
if (CollectionUtils.isNotEmpty(sourceInstances) && sourceInstances.get(0).isEphemeral()) {
- //处临实例的批量数据同步,需要获取当前所有的服务实例,TODO,当Client为1.x的时候,执行和持久化实例一样的同步方式
- handlerPersistenceInstance(taskDO, destNamingService, sourceInstances, level);
+ //处理临时实例批量同步:nacos2批量注册接口采用全量更新方式,实例列表需包含直接注册到源集群的全量实例
+ handlerEphemeralInstance(taskDO, destNamingService, serviceName, groupName, sourceInstances, level);
} else if (CollectionUtils.isEmpty(sourceInstances)) {
- //如果当前源集群是空的 ,那么直接注销目标集群的实例
- log.debug("service {} need sync Ephemeral instance num is null: serviceName ", taskDO.getServiceName());
- processDeRegisterInstances(taskDO, destNamingService);
+ //如果当前源集群是空的 ,那么注销目标集群中来自当前源集群的同步实例
+ log.debug("serviceName {} need sync instance num from cluster {} is null", serviceName, sourceClusterId);
+ processDeRegisterInstances(taskDO, destNamingService, serviceName, groupName);
} else {
- //处临持久化实例的批量数据同步
- handlerPersistenceInstance(taskDO, destNamingService, sourceInstances, level);
+ //处理持久化实例的批量数据同步
+ handlerPersistenceInstance(taskDO, destNamingService, serviceName, groupName, sourceInstances, level);
}
} finally {
syncTaskTap.remove(taskId);
}
}
- private void handlerPersistenceInstance(TaskDO taskDO, NamingService destNamingService,
+ /**
+ * 通过nacos2批量注册接口全量同步源集群需要同步到目标集群指定service的所有临时实例
+ */
+ private void handlerEphemeralInstance(TaskDO taskDO, NamingService destNamingService, String serviceName, String groupName,
+ List sourceInstances, int level) throws NacosException {
+
+ //构建源集群指定service需要同步到目标集群的全量实例列表
+ List needRegisterInstances = new ArrayList<>();
+ String destClusterId = taskDO.getDestClusterId();
+ String sourceClusterId = taskDO.getSourceClusterId();
+ String syncSourceKey = skyWalkerCacheServices.getClusterType(sourceClusterId).getCode();
+ String version = taskDO.getVersion();
+ Set revisions = new HashSet<>(16);
+ for (Instance instance : sourceInstances) {
+ if (needSync(instance.getMetadata(), level, destClusterId)) {
+ Instance syncInstance = buildSyncInstance(instance, serviceName,
+ destClusterId, sourceClusterId, syncSourceKey, version,
+ revisions);
+ log.debug("需要从源集群同步到目标集群的临时实例:{}", syncInstance);
+ needRegisterInstances.add(syncInstance);
+ }
+ }
+
+ if (CollectionUtils.isNotEmpty(needRegisterInstances)) {
+ //批量注册
+ log.debug("将源集群指定service的临时实例全量同步到目标集群: {}", taskDO);
+ destNamingService.batchRegisterInstance(serviceName, groupName, needRegisterInstances);
+
+ //同步实例revision、接口名到应用名的映射关系
+ syncIntanceRevisionAndInterfacesMapping(serviceName, sourceClusterId, destClusterId, revisions);
+ } else {
+ //注销目标集群指定service来自当前源集群同步的所有实例
+ processDeRegisterInstances(taskDO, destNamingService, serviceName, groupName);
+ }
+ }
+
+ /**
+ * 持久实例只需要逐个注册新注册的实例,逐个反注册已注销的实例
+ */
+ private void handlerPersistenceInstance(TaskDO taskDO, NamingService destNamingService, String serviceName, String groupName,
List sourceInstances, int level) throws NacosException {
- List needRegisterInstance = new ArrayList<>();
+
+ // 源集群指定service的全量实例
+ List needRegisterInstances = new ArrayList<>();
+ String destClusterId = taskDO.getDestClusterId();
for (Instance instance : sourceInstances) {
- if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) {
- needRegisterInstance.add(instance);
+ if (needSync(instance.getMetadata(), level, destClusterId)) {
+ needRegisterInstances.add(instance);
}
}
- List destAllInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
- // 获取目标集群自己已经同步的实例
+ // 获取目标集群指定service的全量实例
+ List destAllInstances = destNamingService.getAllInstances(serviceName,
+ groupName, new ArrayList<>(), true);
+
+ // 获取目标集群指定service中来自当前源集群同步的实例
+ String sourceClusterId = taskDO.getSourceClusterId();
List destHasSyncInstances = destAllInstances.stream()
- .filter(instance -> hasSync(instance, taskDO.getSourceClusterId())).collect(Collectors.toList());
+ .filter(instance -> hasSync(instance, sourceClusterId)).collect(Collectors.toList());
+
+ // 获取当前源集群指定service的新增实例(尚未注册到目标集群的实例)、目标集群和源集群同时存在指定service的实例
+ List newInstances = new ArrayList<>(needRegisterInstances);
+ List bothExistedInstances = instanceRemove(destHasSyncInstances, newInstances);
- //获取新增的实例,遍历新增
- List newInstances = new ArrayList<>(needRegisterInstance);
- instanceRemove(destHasSyncInstances, newInstances);
- //注册
+ // 逐个注册源集群新增的实例到目标集群
+ String syncSourceKey = skyWalkerCacheServices.getClusterType(sourceClusterId).getCode();
+ String version = taskDO.getVersion();
+ Set revisions = new HashSet<>(16);
for (Instance newInstance : newInstances) {
- destNamingService.registerInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
- buildSyncInstance(newInstance, taskDO));
+ Instance syncInstance = buildSyncInstance(newInstance, serviceName,
+ destClusterId, sourceClusterId, syncSourceKey, version,
+ revisions);
+ log.debug("从源集群同步到目标集群的持久实例:{}", syncInstance);
+ destNamingService.registerInstance(serviceName, groupName, syncInstance);
}
- List notRemoveInstances = new ArrayList<>();
- for (Instance destHasSyncInstance : destHasSyncInstances) {
- for (Instance instance : needRegisterInstance) {
- if (instanceEquals(destHasSyncInstance, instance)) {
- notRemoveInstances.add(destHasSyncInstance);
- }
- }
- }
- destHasSyncInstances.removeAll(notRemoveInstances);
+ //同步实例revision、接口名到应用名的映射关系
+ syncIntanceRevisionAndInterfacesMapping(serviceName, sourceClusterId, destClusterId, revisions);
- if (CollectionUtils.isNotEmpty(destHasSyncInstances)) {
- log.info("taskid:{},服务 {} 发生反注册,执行数量 {} ", taskDO.getTaskId(), taskDO.getServiceName(),
- destHasSyncInstances.size());
- }
+ // 获取目标集群来自当前源集群同步的指定service实例中需要注销的实例(实例在源集群中已注销)
+ destHasSyncInstances.removeAll(bothExistedInstances);
- for (Instance destAllInstance : destHasSyncInstances) {
- destNamingService.deregisterInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
- destAllInstance);
+ if (CollectionUtils.isNotEmpty(destHasSyncInstances)) {
+ log.info("taskid:{},服务 {} 发生反注册,执行数量 {} ", taskDO.getTaskId(), serviceName, destHasSyncInstances.size());
+ for (Instance needDeregisterInstance : destHasSyncInstances) {
+ removeUnwantedAttrsForNacosRedo(needDeregisterInstance);
+ log.debug("逐个反注册持久实例: {}", needDeregisterInstance);
+ destNamingService.deregisterInstance(serviceName, groupName, needDeregisterInstance);
+ }
}
}
-
public static boolean instanceEquals(Instance ins1, Instance ins2) {
return (ins1.getIp().equals(ins2.getIp())) && (ins1.getPort() == ins2.getPort()) && (ins1.getWeight()
== ins2.getWeight()) && (ins1.isHealthy() == ins2.isHealthy()) && (ins1.isEphemeral()
@@ -344,18 +451,24 @@ public static boolean instanceEquals(Instance ins1, Instance ins2) {
&& (ins1.getServiceName().equals(ins2.getServiceName()));
}
- private void instanceRemove(List destHasSyncInstances, List newInstances) {
- List needRemoveInstance = new ArrayList<>();
+ /**
+ * 获取新增实例:从源集群指定service需要同步到目标集群的全量实例列表中排除目标集群中已存在的来自当前源集群同步的实例,
+ * 返回目标集群和源集群均存在的实例
+ */
+ private List instanceRemove(List destHasSyncInstances, List newInstances) {
+ List bothExistedInstances = new ArrayList<>();
for (Instance destHasSyncInstance : destHasSyncInstances) {
for (Instance newInstance : newInstances) {
- if (destHasSyncInstance.equals(newInstance)) {
+ // fix bug: 目标集群同步实例元数据比源集群实例元数据多了SOURCE_CLUSTERID_KEY等数据,不能用Instance#equals比较
+ if (instanceEquals(destHasSyncInstance, newInstance)) {
//如果目标集群已经存在了源集群同步过来的实例,就不需要同步了
- needRemoveInstance.add(newInstance);
+ bothExistedInstances.add(newInstance);
}
}
}
// eg:A Cluster 已经同步到 B Cluster的实例数据,就不需要再重复同步过来了
- newInstances.removeAll(needRemoveInstance);
+ newInstances.removeAll(bothExistedInstances);
+ return bothExistedInstances;
}
private boolean hasSync(Instance instance, String sourceClusterId) {
@@ -366,7 +479,6 @@ private boolean hasSync(Instance instance, String sourceClusterId) {
return false;
}
-
/**
* 当源集群需要同步的实例个数为0时,目标集群如果还有源集群同步的实例,执行反注册
*
@@ -374,40 +486,60 @@ private boolean hasSync(Instance instance, String sourceClusterId) {
* @param destNamingService
* @throws NacosException
*/
- private void processDeRegisterInstances(TaskDO taskDO, NamingService destNamingService) throws NacosException {
- //如果此时sourceInstance中的实例为空,证明此时实例下线或实例不存在
- List destInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), false);
- // 如果目标集群中的数据实例也为空了,则测试无需操作
+ private void processDeRegisterInstances(TaskDO taskDO, NamingService destNamingService,
+ String serviceName, String groupName) throws NacosException {
+ List destInstances = destNamingService.getAllInstances(serviceName,
+ groupName, new ArrayList<>(), false);
+ // 如果目标集群中的数据实例也为空了,则无需操作
if (CollectionUtils.isEmpty(destInstances)) {
return;
}
- destInstances = deRegisterFilter(destInstances, taskDO.getSourceClusterId());
- if (CollectionUtils.isNotEmpty(destInstances)) {
- //逐个执行反注册,拿出一个实例即可, 需要处理redo,否则会被重新注册上来
- for (Instance destInstance : destInstances) {
- destNamingService.deregisterInstance(taskDO.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), destInstance);
- }
- }
+ // 过滤出目标集群中来自当前源集群的同步实例中需要进行注销的实例
+ List needDeregisterInstances = deRegisterFilter(destInstances, taskDO.getSourceClusterId());
+ // 反注册注销实例
+ doDeregisterInstance(taskDO, destNamingService, serviceName, groupName, needDeregisterInstances);
}
private List deRegisterFilter(List destInstances, String sourceClusterId) {
- List newDestInstance = new ArrayList<>();
+ List needDeregisterInstances = new ArrayList<>();
for (Instance destInstance : destInstances) {
Map metadata = destInstance.getMetadata();
String destSourceClusterId = metadata.get(SkyWalkerConstants.SOURCE_CLUSTERID_KEY);
if (needDeregister(destSourceClusterId, sourceClusterId)) {
- // 需要执行反注册
- newDestInstance.add(destInstance);
+ removeUnwantedAttrsForNacosRedo(destInstance);
+ log.debug("需要反注册的实例: {}", destInstance);
+ needDeregisterInstances.add(destInstance);
+ }
+ }
+ return needDeregisterInstances;
+ }
+
+ public static void doDeregisterInstance(TaskDO taskDO, NamingService destNamingService, String serviceName, String groupName,
+ List instances) throws NacosException {
+ if (CollectionUtils.isNotEmpty(instances)) {
+ if (instances.get(0).isEphemeral()) {
+ log.debug("批量反注册来自源集群的同步实例: {}", taskDO);
+ destNamingService.batchDeregisterInstance(serviceName, groupName, instances);
+ } else {
+ // 目前nacos2提供的批量反注册接口不支持持久实例,因此只能逐个反注册
+ for (Instance instance : instances) {
+ log.debug("逐个反注册来自源集群的同步实例: {}", instance);
+ destNamingService.deregisterInstance(serviceName, groupName, instance);
+ }
}
}
- return newDestInstance;
}
- private boolean needDeregister(String destClusterId, String sourceClusterId) {
- if (!StringUtils.isEmpty(destClusterId)) {
- return destClusterId.equals(sourceClusterId);
+ public static void removeUnwantedAttrsForNacosRedo(Instance instance) {
+ //清空查询实例返回的instanceId以保证nacos批量注册接口正常匹配redo缓存(nacos-sync调用批量注册接口时未设置instanceId,redo缓存实例对象的instanceId属性为null)
+ instance.setInstanceId(null);
+ //清空查询实例返回的serviceName(nacos2.x查询实例返回的serviceName包含组名,nacos2.x批量接口参数检验规则要求服务名不能包含组名)
+ instance.setServiceName(null);
+ }
+
+ private boolean needDeregister(String destSourceClusterId, String sourceClusterId) {
+ if (!StringUtils.isEmpty(destSourceClusterId)) {
+ return destSourceClusterId.equals(sourceClusterId);
}
return false;
}
@@ -418,7 +550,7 @@ private boolean needSync(Map sourceMetaData, int level, String d
return SyncService.super.needSync(sourceMetaData);
}
//中心集群,只要不是目标集群传过来的实例,都需要同步(扩展功能)
- if (!destClusterId.equals(sourceMetaData.get(SOURCE_CLUSTERID_KEY))) {
+ if (!destClusterId.equals(sourceMetaData.get(SkyWalkerConstants.SOURCE_CLUSTERID_KEY))) {
return true;
}
return false;
@@ -447,26 +579,272 @@ private static String buildClientKey(TaskDO taskDO) {
return taskDO.getId() + ":" + taskDO.getServiceName();
}
- private Instance buildSyncInstance(Instance instance, TaskDO taskDO) {
+ private Instance buildSyncInstance(Instance instance, String serviceName,
+ String destClusterId, String sourceClusterId, String syncSourceKey, String version,
+ Set revisions) {
+
+ //收集需要同步的实例revision
+ collectInstanceRevision(instance, serviceName, revisions);
+
Instance temp = new Instance();
temp.setIp(instance.getIp());
temp.setPort(instance.getPort());
temp.setClusterName(instance.getClusterName());
- temp.setServiceName(instance.getServiceName());
+ //查询源集群实例返回的serviceName含组名前缀,但Nacos2服务端检查批量注册请求serviceName参数时不能包含组名前缀,因此注册实例到目标集群时不再设置serviceName。
temp.setEnabled(instance.isEnabled());
temp.setHealthy(instance.isHealthy());
temp.setWeight(instance.getWeight());
temp.setEphemeral(instance.isEphemeral());
Map metaData = new HashMap<>(instance.getMetadata());
- metaData.put(SkyWalkerConstants.DEST_CLUSTERID_KEY, taskDO.getDestClusterId());
- metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY,
- skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
- metaData.put(SkyWalkerConstants.SOURCE_CLUSTERID_KEY, taskDO.getSourceClusterId());
+ metaData.put(SkyWalkerConstants.DEST_CLUSTERID_KEY, destClusterId);
+ metaData.put(SkyWalkerConstants.SOURCE_CLUSTERID_KEY, sourceClusterId);
+ metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY, syncSourceKey);
//标识是同步实例
- metaData.put(SkyWalkerConstants.SYNC_INSTANCE_TAG, taskDO.getSourceClusterId() + "@@" + taskDO.getVersion());
+ metaData.put(SkyWalkerConstants.SYNC_INSTANCE_TAG, sourceClusterId + "@@" + version);
temp.setMetadata(metaData);
return temp;
}
-
-
+
+ private void collectInstanceRevision(Instance instance, String serviceName, Set revisions) {
+ if (serviceName.startsWith(DubboConstants.CATALOG_KEY + DubboConstants.SEPARATOR_KEY)) {
+ //dubbo接口级别服务实例
+ return;
+ }
+
+ String storeType = instance.getMetadata().get(DubboConstants.METADATA_STORAGE_TYPE_KEY);
+ if (!DubboConstants.METADATA_STORAGE_TYPE_REMOTE.equals(storeType)) {
+ return;
+ }
+
+ //dubbo.metadata.storage-type=remote: 收集需要同步的应用级别服务实例revision
+ String revision = instance.getMetadata().get(DubboConstants.METADATA_REVISION_KEY);
+ if (revision == null || revisions.contains(revision)) {
+ return;
+ }
+ revisions.add(revision);
+ }
+
+ private void syncIntanceRevisionAndInterfacesMapping(String serviceName,
+ String sourceClusterId, String destClusterId, Set revisions) {
+ if (serviceName.startsWith(DubboConstants.CATALOG_KEY + DubboConstants.SEPARATOR_KEY)) {
+ //dubbo接口级别服务实例
+ return;
+ }
+
+ ConfigService sourceConfigService = nacosServerHolder.getConfigService(sourceClusterId);
+ ConfigService destConfigService = nacosServerHolder.getConfigService(destClusterId);
+ if (sourceConfigService == null || destConfigService == null) {
+ return;
+ }
+
+ Set interfaceNames = new HashSet<>(16);
+ if (revisions.isEmpty()) {
+ //通过查询源集群mapping元数据收集应用接口名
+ collectInterfaceNamesByQueryMetaData(serviceName, sourceClusterId, interfaceNames);
+ } else {
+ //同步revision元数据,收集应用接口名
+ syncInstanceRevisionAndCollectInterfaceNames(serviceName,
+ destClusterId, sourceClusterId,
+ destConfigService, sourceConfigService,
+ revisions, interfaceNames);
+ if (interfaceNames.isEmpty()) {
+ //查询源集群mapping获取接口名: provider配置的dubbo.application.metadata-type为local时,默认不登记revision元数据
+ collectInterfaceNamesByQueryMetaData(serviceName, sourceClusterId, interfaceNames);
+ }
+ }
+
+ //同步接口名与应用名的映射关系
+ for (String interfaceName : interfaceNames) {
+ try {
+ String appNameStr = destConfigService.getConfig(interfaceName, DubboConstants.METADATA_MAPPING_KEY,
+ GET_CFG_TIMEOUT);
+ if (appNameStr == null) {
+ destConfigService.publishConfig(interfaceName, DubboConstants.METADATA_MAPPING_KEY, serviceName);
+ } else {
+ boolean hasPublished = false;
+ String[] appNames = appNameStr.split(",");
+ for (String appName : appNames) {
+ if (serviceName.equals(appName)) {
+ hasPublished = true;
+ break;
+ }
+ }
+ if (hasPublished) {
+ return;
+ }
+ String lastMd5 = MD5Utils.md5Hex(appNameStr, StandardCharsets.UTF_8.name());
+ destConfigService.publishConfigCas(interfaceName, DubboConstants.METADATA_MAPPING_KEY,
+ serviceName + "," + appNameStr, lastMd5);
+ }
+ } catch (NacosException e) {
+ log.error("sync interface mapping fail,service:{},interface:{},destClusterId:{}", serviceName,
+ interfaceName, destClusterId, e);
+ }
+ }
+ }
+
+ private void collectInterfaceNamesByQueryMetaData(String serviceName,
+ String sourceClusterId, Set interfaceNames) {
+ MappingMetaDataCache metaDataCache = MAPPING_METADATA_CACHE.get(sourceClusterId);
+ if (metaDataCache != null && System.currentTimeMillis() < metaDataCache.getExpiredTime()) {
+ //当元数据缓存有效时,从元数据缓存收集接口名
+ MappingMetaData metaData = metaDataCache.getMetaData();
+ collectInterfaceNamesFromMappingMetaData(serviceName, metaData, interfaceNames);
+ if (!interfaceNames.isEmpty()) {
+ return;
+ }
+ }
+
+ NamingHttpClientProxy namingHttpClientProxy = nacosServerHolder.getNamingHttpProxy(sourceClusterId);
+ if (namingHttpClientProxy == null) {
+ log.error("clusterid: {} null namingHttpClientProxy!", sourceClusterId);
+ return;
+ }
+
+ //查询元数据中心
+ MappingMetaData metaData = queryMappingMetaData(sourceClusterId, namingHttpClientProxy);
+ if (metaData == null) {
+ return;
+ }
+ metaDataCache = new MappingMetaDataCache();
+ metaDataCache.setMetaData(metaData);
+ metaDataCache.setExpiredTime(System.currentTimeMillis() + METADATA_CACHE_LIVE_MS);
+ MAPPING_METADATA_CACHE.put(sourceClusterId, metaDataCache);
+
+ collectInterfaceNamesFromMappingMetaData(serviceName, metaData, interfaceNames);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void syncInstanceRevisionAndCollectInterfaceNames(String serviceName,
+ String destClusterId, String sourceClusterId,
+ ConfigService destConfigService, ConfigService sourceConfigService,
+ Set revisions, Set interfaceNames) {
+ for (String revision : revisions) {
+ String content;
+ try {
+ content = sourceConfigService.getConfig(serviceName, revision, GET_CFG_TIMEOUT);
+ if (content == null) {
+ continue;
+ }
+ } catch (NacosException e) {
+ log.error("get instance revision fail,service:{},revision:{},sourceClusterId:{}", serviceName,
+ revision, sourceClusterId, e);
+ continue;
+ }
+
+ try {
+ destConfigService.publishConfig(serviceName, revision, content);
+ } catch (NacosException e) {
+ log.error("publish instance revision fail,service:{},revision:{},destClusterId:{}", serviceName,
+ revision, sourceClusterId, e);
+ continue;
+ }
+
+ Map metaDataJson = null;
+ try {
+ metaDataJson = GSON.fromJson(content, Map.class);
+ if (metaDataJson == null) {
+ continue;
+ }
+ } catch (JsonSyntaxException ex) {
+ log.error("parse json content fail,content:{},service:{},revision:{},sourceClusterId:{}", content,
+ serviceName, revision, sourceClusterId, ex);
+ continue;
+ }
+
+ // 收集当前应用服务的全部接口名称
+ Map serviceMetaDataMap = (Map) metaDataJson
+ .get(DubboConstants.METADATA_SERVICES_KEY);
+ for (Map.Entry entry : serviceMetaDataMap.entrySet()) {
+ Map serviceMetaData = (Map) entry.getValue();
+ if (serviceMetaData == null) {
+ continue;
+ }
+ String interfaceName = (String) serviceMetaData.get(DubboConstants.METADATA_NAME_KEY);
+ if (interfaceName == null) {
+ continue;
+ }
+ interfaceNames.add(interfaceName);
+ }
+ }
+ }
+
+ private void collectInterfaceNamesFromMappingMetaData(String serviceName,
+ MappingMetaData metaData, Set interfaceNames) {
+ for (MappingItem item : metaData.pageItems) {
+ String appNamesContent = item.getContent();
+ if (appNamesContent == null) {
+ continue;
+ }
+ String[] appNames = appNamesContent.split(",");
+ for (String appName : appNames) {
+ if (serviceName.equals(appName)) {
+ interfaceNames.add(item.getDataId());
+ break;
+ }
+ }
+ }
+ }
+
+ private MappingMetaData queryMappingMetaData(String sourceClusterId,
+ NamingHttpClientProxy namingHttpClientProxy) {
+ int pageNo = 1;
+ final MappingMetaData metaData = queryMappingMetaDataByPaging(sourceClusterId,
+ namingHttpClientProxy, pageNo, PAGE_SIZE);
+ if (metaData == null) {
+ return null;
+ }
+ MappingMetaData tmpMetaData = metaData;
+ while (tmpMetaData.pageItems.size() >= PAGE_SIZE) {
+ pageNo++;
+ tmpMetaData = queryMappingMetaDataByPaging(sourceClusterId,
+ namingHttpClientProxy, pageNo, PAGE_SIZE);
+ if (tmpMetaData == null) {
+ break;
+ }
+ metaData.pageItems.addAll(tmpMetaData.pageItems);
+ }
+ return metaData;
+ }
+
+ private MappingMetaData queryMappingMetaDataByPaging(String sourceClusterId,
+ NamingHttpClientProxy namingHttpClientProxy, int pageNo, int pageSize) {
+ Map params = new HashMap<>(GET_MAPPING_CFG_BASE_PARAMS);
+ params.put(PAGE_NO_KEY, String.valueOf(pageNo));
+ params.put(PAGE_SIZE_KEY, String.valueOf(pageSize));
+ String metaDataString = null;
+ try {
+ metaDataString = namingHttpClientProxy.reqApi(GET_MAPPING_CFG_URL,
+ params, HttpMethod.GET);
+ } catch (NacosException e) {
+ log.error("query mapping metadata from: {} failed.", sourceClusterId, e);
+ return null;
+ }
+ MappingMetaData metaData;
+ try {
+ metaData = JacksonUtils.toObj(metaDataString, MappingMetaData.class);
+ } catch (NacosDeserializationException e) {
+ log.error("parse mapping metadata: {} from: {} failed.", metaDataString, sourceClusterId, e);
+ return null;
+ }
+ return metaData;
+ }
+
+ @Data
+ private static class MappingItem {
+ private String dataId;
+ private String group;
+ private String content;
+ }
+
+ @Data
+ private static class MappingMetaData {
+ private List pageItems;
+ }
+
+ @Data
+ private static class MappingMetaDataCache {
+ private MappingMetaData metaData;
+ private long expiredTime;
+ }
}
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ZookeeperSyncToNacosServiceImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ZookeeperSyncToNacosServiceImpl.java
index d2747723..a465e4f3 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ZookeeperSyncToNacosServiceImpl.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/ZookeeperSyncToNacosServiceImpl.java
@@ -26,6 +26,7 @@
import com.alibaba.nacossync.extension.holder.ZookeeperServerHolder;
import com.alibaba.nacossync.monitor.MetricsManager;
import com.alibaba.nacossync.pojo.model.TaskDO;
+
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
@@ -33,13 +34,13 @@
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.utils.CloseableUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
@@ -138,19 +139,53 @@ private void processEvent(TaskDO taskDO, NamingService destNamingService, TreeCa
Map ipAndPortParam = parseIpAndPortString(path);
Instance instance = buildSyncInstance(queryParam, ipAndPortParam, taskDO);
String serviceName = queryParam.get(INTERFACE_KEY);
+ String groupName = getGroupNameOrDefault(taskDO.getGroupName());
switch (event.getType()) {
case NODE_ADDED:
case NODE_UPDATED:
-
- destNamingService.registerInstance(getServiceNameFromCache(serviceName, queryParam),
- getGroupNameOrDefault(taskDO.getGroupName()), instance);
+ String registerNacosServiceName = getServiceNameFromCache(serviceName, queryParam);
+ if (instance.isEphemeral()) {
+ List destAllInstances = destNamingService.getAllInstances(registerNacosServiceName,
+ groupName, new ArrayList<>(), true);
+ List needRegisterInstances = new ArrayList<>();
+ for (Instance destInstance : destAllInstances) {
+ if (needDelete(destInstance.getMetadata(), taskDO)) {
+ // 从当前源集群同步到目标集群的实例
+ if (!instanceEquals(instance, destInstance) ) {
+ log.debug("需要从源集群同步到目标集群的临时实例:{}", destInstance);
+ needRegisterInstances.add(destInstance);
+ }
+ }
+ }
+ log.debug("需要从源集群更新到目标集群的临时实例:{}", instance);
+ needRegisterInstances.add(instance);
+ log.debug("将源集群指定service的临时实例全量同步到目标集群的{}: {}", registerNacosServiceName, taskDO);
+ destNamingService.batchRegisterInstance(registerNacosServiceName, groupName, needRegisterInstances);
+ } else {
+ log.debug("将源集群指定service的持久实例{}同步到目标集群的{}: {}", instance, registerNacosServiceName, taskDO);
+ destNamingService.registerInstance(registerNacosServiceName, groupName, instance);
+ }
break;
case NODE_REMOVED:
-
- destNamingService.deregisterInstance(getServiceNameFromCache(serviceName, queryParam),
- getGroupNameOrDefault(taskDO.getGroupName()), ipAndPortParam.get(INSTANCE_IP_KEY),
+ String deregisterNacosServiceName = getServiceNameFromCache(serviceName, queryParam);
+ log.debug("反注册目标集群的{}: {}", deregisterNacosServiceName, taskDO);
+ destNamingService.deregisterInstance(deregisterNacosServiceName,
+ groupName, ipAndPortParam.get(INSTANCE_IP_KEY),
Integer.parseInt(ipAndPortParam.get(INSTANCE_PORT_KEY)));
- nacosServiceNameMap.remove(serviceName);
+ List destAllInstances = destNamingService.getAllInstances(deregisterNacosServiceName,
+ groupName, new ArrayList<>(), true);
+ boolean hasSyncInstancesFromTaskSource = false;
+ for (Instance destInstance : destAllInstances) {
+ if (needDelete(destInstance.getMetadata(), taskDO)) {
+ // 目标集群还有当前同步任务的源集群同步的实例
+ hasSyncInstancesFromTaskSource = true;
+ break;
+ }
+ }
+ if (!hasSyncInstancesFromTaskSource) {
+ // 目标集群没有当前同步任务的源集群同步的实例
+ nacosServiceNameMap.remove(serviceName);
+ }
break;
default:
break;
@@ -176,62 +211,91 @@ private void registerALLInstances0(TaskDO taskDO, NamingService destNamingServic
if (zk.getChildren() == null) {
return;
}
+ Map> needRegisterInstanceMap = new HashMap<>();
List providers = zk.getChildren().forPath(path);
for (String provider : providers) {
Map queryParam = parseQueryString(provider);
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map ipAndPortParam = parseIpAndPortString(path + ZOOKEEPER_SEPARATOR + provider);
Instance instance = buildSyncInstance(queryParam, ipAndPortParam, taskDO);
- destNamingService.registerInstance(getServiceNameFromCache(serviceName, queryParam),
- getGroupNameOrDefault(taskDO.getGroupName()), instance);
+ String nacosServiceName = getServiceNameFromCache(serviceName, queryParam);
+ List needRegisterInstances = needRegisterInstanceMap.get(nacosServiceName);
+ if (needRegisterInstances == null) {
+ needRegisterInstances = new ArrayList<>();
+ needRegisterInstances.add(instance);
+ needRegisterInstanceMap.put(nacosServiceName, needRegisterInstances);
+ } else {
+ needRegisterInstances.add(instance);
+ }
+ }
+ }
+ String groupName = getGroupNameOrDefault(taskDO.getGroupName());
+ for (Map.Entry> entry : needRegisterInstanceMap.entrySet()) {
+ String nacosServiceName = entry.getKey();
+ List needRegisterInstances = entry.getValue();
+ if (needRegisterInstances.get(0).isEphemeral()) {
+ // 批量注册
+ log.debug("将源集群指定服务的临时实例全量同步到目标集群的{}: {}", nacosServiceName, taskDO);
+ destNamingService.batchRegisterInstance(nacosServiceName, groupName, needRegisterInstances);
+ } else {
+ for (Instance instance : needRegisterInstances) {
+ log.debug("从源集群同步指定服务到目标集群的{}:{}", nacosServiceName, instance);
+ destNamingService.registerInstance(nacosServiceName, groupName, instance);
+ }
}
}
}
@Override
public boolean delete(TaskDO taskDO) {
- if (taskDO.getServiceName() == null) {
- return true;
- }
try {
-
+ String serviceName = taskDO.getServiceName();
+ String groupName = getGroupNameOrDefault(taskDO.getGroupName());
CloseableUtils.closeQuietly(treeCacheMap.get(taskDO.getTaskId()));
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
- if (!ALL_SERVICE_NAME_PATTERN.equals(taskDO.getServiceName())) {
- if (nacosServiceNameMap.containsKey(taskDO.getServiceName())) {
- List allInstances = destNamingService.getAllInstances(
- nacosServiceNameMap.get(taskDO.getServiceName()),
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
- for (Instance instance : allInstances) {
- if (needDelete(instance.getMetadata(), taskDO)) {
- destNamingService.deregisterInstance(instance.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
- }
- nacosServiceNameMap.remove(taskDO.getServiceName());
-
+ if (!ALL_SERVICE_NAME_PATTERN.equals(serviceName)) {
+ String nacosServiceName = nacosServiceNameMap.get(serviceName);
+ if (nacosServiceName == null) {
+ return true;
+ }
+ List allInstances = destNamingService.getAllInstances(
+ nacosServiceName, groupName, new ArrayList<>(), true);
+ List needDeregisterInstances = new ArrayList<>();
+ for (Instance instance : allInstances) {
+ if (needDelete(instance.getMetadata(), taskDO)) {
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
+ if (CollectionUtils.isEmpty(needDeregisterInstances)) {
+ nacosServiceNameMap.remove(serviceName);
+ return true;
+ }
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, nacosServiceName,
+ groupName, needDeregisterInstances);
+ nacosServiceNameMap.remove(serviceName);
} else {
- Set serviceNames = nacosServiceNameMap.keySet();
- for (String serviceName : serviceNames) {
-
- if (nacosServiceNameMap.containsKey(serviceName)) {
- List allInstances = destNamingService.getAllInstances(serviceName,
- getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
- for (Instance instance : allInstances) {
- if (needDelete(instance.getMetadata(), taskDO)) {
- destNamingService.deregisterInstance(instance.getServiceName(),
- getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(),
- instance.getPort());
- }
- nacosServiceNameMap.remove(serviceName);
-
+ for (Map.Entry entry : nacosServiceNameMap.entrySet()) {
+ String nacosServiceName = entry.getValue();
+ List allInstances = destNamingService.getAllInstances(nacosServiceName,
+ groupName, new ArrayList<>(), true);
+ List needDeregisterInstances = new ArrayList<>();
+ for (Instance instance : allInstances) {
+ if (needDelete(instance.getMetadata(), taskDO)) {
+ NacosSyncToNacosServiceImpl.removeUnwantedAttrsForNacosRedo(instance);
+ log.debug("需要反注册的实例: {}", instance);
+ needDeregisterInstances.add(instance);
}
}
+ if (CollectionUtils.isEmpty(needDeregisterInstances)) {
+ continue;
+ }
+ NacosSyncToNacosServiceImpl.doDeregisterInstance(taskDO, destNamingService, nacosServiceName,
+ groupName, needDeregisterInstances);
}
+ nacosServiceNameMap.clear();
}
-
-
} catch (Exception e) {
log.error("delete task from zookeeper to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR);
@@ -285,7 +349,7 @@ protected Instance buildSyncInstance(Map queryParam, Map queryParam, Map queryParam) {
return nacosServiceNameMap.computeIfAbsent(serviceName, (key) -> createServiceName(queryParam));
}
+
+ public static boolean instanceEquals(Instance ins1, Instance ins2) {
+ return (ins1.getIp().equals(ins2.getIp())) && (ins1.getPort() == ins2.getPort());
+ }
}
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskAddAllProcessor.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskAddAllProcessor.java
index c02cb676..67f072fa 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskAddAllProcessor.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskAddAllProcessor.java
@@ -21,9 +21,8 @@
import com.alibaba.nacos.api.naming.CommonParams;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.client.naming.NacosNamingService;
-import com.alibaba.nacos.client.naming.net.NamingProxy;
+import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
-import com.alibaba.nacos.common.utils.HttpMethod;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacossync.constant.TaskStatusEnum;
@@ -45,6 +44,8 @@
import org.springframework.util.ReflectionUtils;
import javax.annotation.Nullable;
+import javax.ws.rs.HttpMethod;
+
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
@@ -98,12 +99,14 @@ public void process(TaskAddAllRequest addAllRequest, TaskAddResult taskAddResult
throw new SkyWalkerException("current sync type not supported.");
}
// TODO 目前仅支持 Nacos 为源的同步类型,待完善更多类型支持。
- final NamingService sourceNamingService = nacosServerHolder.get(sourceCluster.getClusterId());
- if (sourceNamingService == null) {
+ String sourceClusterId = sourceCluster.getClusterId();
+ final NamingService sourceNamingService = nacosServerHolder.get(sourceClusterId);
+ final NamingHttpClientProxy sourceNamingHttpClientProxy = nacosServerHolder.getNamingHttpProxy(sourceClusterId);
+ if (sourceNamingService == null || sourceNamingHttpClientProxy == null) {
throw new SkyWalkerException("only support sync type that the source of the Nacos.");
}
- final EnhanceNamingService enhanceNamingService = new EnhanceNamingService(sourceNamingService);
+ final EnhanceNamingService enhanceNamingService = new EnhanceNamingService(sourceNamingService, sourceNamingHttpClientProxy);
final CatalogServiceResult catalogServiceResult = enhanceNamingService.catalogServices(null, null);
if (catalogServiceResult == null || catalogServiceResult.getCount() <= 0) {
throw new SkyWalkerException("sourceCluster data empty");
@@ -151,20 +154,22 @@ static class EnhanceNamingService {
protected NamingService delegate;
- protected NamingProxy serverProxy;
+ protected NamingHttpClientProxy httpClientProxy;
+
+ private final String namespaceId;
- protected EnhanceNamingService(NamingService namingService) {
+ protected EnhanceNamingService(NamingService namingService, NamingHttpClientProxy namingHttpClientProxy) {
if (!(namingService instanceof NacosNamingService)) {
throw new IllegalArgumentException(
"namingService only support instance of com.alibaba.nacos.client.naming.NacosNamingService.");
}
this.delegate = namingService;
- // serverProxy
- final Field serverProxyField = ReflectionUtils.findField(NacosNamingService.class, "serverProxy");
- assert serverProxyField != null;
- ReflectionUtils.makeAccessible(serverProxyField);
- this.serverProxy = (NamingProxy) ReflectionUtils.getField(serverProxyField, delegate);
+ this.httpClientProxy = namingHttpClientProxy;
+ final Field namespaceIdField = ReflectionUtils.findField(NamingHttpClientProxy.class, "namespaceId");
+ assert namespaceIdField != null;
+ ReflectionUtils.makeAccessible(namespaceIdField);
+ this.namespaceId = (String) ReflectionUtils.getField(namespaceIdField, this.httpClientProxy);
}
public CatalogServiceResult catalogServices(@Nullable String serviceName, @Nullable String group)
@@ -199,13 +204,13 @@ public CatalogServiceResult catalogServices(@Nullable String serviceName, @Nulla
// serviceNameParam
// groupNameParam
final Map params = new HashMap<>(8);
- params.put(CommonParams.NAMESPACE_ID, serverProxy.getNamespaceId());
+ params.put(CommonParams.NAMESPACE_ID, this.namespaceId);
params.put(SERVICE_NAME_PARAM, serviceName);
params.put(GROUP_NAME_PARAM, group);
params.put(PAGE_NO, String.valueOf(pageNo));
params.put(PAGE_SIZE, String.valueOf(pageSize));
- final String result = this.serverProxy.reqApi(UtilAndComs.nacosUrlBase + "/catalog/services", params,
+ final String result = this.httpClientProxy.reqApi(UtilAndComs.nacosUrlBase + "/catalog/services", params,
HttpMethod.GET);
if (StringUtils.isNotEmpty(result)) {
return JacksonUtils.toObj(result, CatalogServiceResult.class);
diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/util/DubboConstants.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/util/DubboConstants.java
index b3a64e09..8cde1dff 100644
--- a/nacossync-worker/src/main/java/com/alibaba/nacossync/util/DubboConstants.java
+++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/util/DubboConstants.java
@@ -50,6 +50,13 @@ public final class DubboConstants {
public static final String DUBBO_ROOT_PATH = "/dubbo";
public static final String ALL_SERVICE_NAME_PATTERN = "*";
+ public static final String METADATA_REVISION_KEY = "dubbo.metadata.revision";
+ public static final String METADATA_SERVICES_KEY = "services";
+ public static final String METADATA_MAPPING_KEY = "mapping";
+ public static final String METADATA_NAME_KEY = "name";
+ public static final String METADATA_STORAGE_TYPE_KEY = "dubbo.metadata.storage-type";
+ public static final String METADATA_STORAGE_TYPE_REMOTE = "remote";
+
/**
* if Dubbo version greater than 2.7.2, service name is providers:interface:version:
* if Dubbo version less than 2.7.2, service name is providers:interface:version
diff --git a/nacossync-worker/src/main/resources/static/js/main.b73436b5.js b/nacossync-worker/src/main/resources/static/js/main.b73436b5.js
index c71dada4..a8da6001 100644
--- a/nacossync-worker/src/main/resources/static/js/main.b73436b5.js
+++ b/nacossync-worker/src/main/resources/static/js/main.b73436b5.js
@@ -325,4 +325,4 @@ var t;e.defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_六月_
* @author Feross Aboukhadijeh
* @license MIT
*/
-e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";var r=n(90),a=n(20),o=n(463),i=n(464);function s(e){this.defaults=e,this.interceptors={request:new o,response:new o}}s.prototype.request=function(e){"string"==typeof e&&(e=a.merge({url:arguments[0]},arguments[1])),(e=a.merge(r,{method:"get"},this.defaults,e)).method=e.method.toLowerCase();var t=[i,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},a.forEach(["delete","get","head","options"],function(n){s.prototype[n]=function(e,t){return this.request(a.merge(t||{},{method:n,url:e}))}}),a.forEach(["post","put","patch"],function(r){s.prototype[r]=function(e,t,n){return this.request(a.merge(n||{},{method:r,url:e,data:t}))}}),e.exports=s},function(e,t,n){"use strict";var a=n(20);e.exports=function(n,r){a.forEach(n,function(e,t){t!==r&&t.toUpperCase()===r.toUpperCase()&&(n[r]=e,delete n[t])})}},function(e,t,n){"use strict";var a=n(286);e.exports=function(e,t,n){var r=n.config.validateStatus;n.status&&r&&!r(n.status)?t(a("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";e.exports=function(e,t,n,r,a){return e.config=t,n&&(e.code=n),e.request=r,e.response=a,e}},function(e,t,n){"use strict";var a=n(20);function o(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,n){if(!t)return e;var r,t=n?n(t):a.isURLSearchParams(t)?t.toString():(r=[],a.forEach(t,function(e,t){null!=e&&(a.isArray(e)?t+="[]":e=[e],a.forEach(e,function(e){a.isDate(e)?e=e.toISOString():a.isObject(e)&&(e=JSON.stringify(e)),r.push(o(t)+"="+o(e))}))}),r.join("&"));return t&&(e+=(-1===e.indexOf("?")?"?":"&")+t),e}},function(e,t,n){"use strict";var a=n(20),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,r={};return e&&a.forEach(e.split("\n"),function(e){n=e.indexOf(":"),t=a.trim(e.substr(0,n)).toLowerCase(),n=a.trim(e.substr(n+1)),t&&(r[t]&&0<=o.indexOf(t)||(r[t]="set-cookie"===t?(r[t]||[]).concat([n]):r[t]?r[t]+", "+n:n))}),r}},function(e,t,n){"use strict";var r,a,o,i=n(20);function s(e){return a&&(o.setAttribute("href",e),e=o.href),o.setAttribute("href",e),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}e.exports=i.isStandardBrowserEnv()?(a=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a"),r=s(window.location.href),function(e){e=i.isString(e)?s(e):e;return e.protocol===r.protocol&&e.host===r.host}):function(){return!0}},function(e,t,n){"use strict";var s=n(20);e.exports=s.isStandardBrowserEnv()?{write:function(e,t,n,r,a,o){var i=[];i.push(e+"="+encodeURIComponent(t)),s.isNumber(n)&&i.push("expires="+new Date(n).toGMTString()),s.isString(r)&&i.push("path="+r),s.isString(a)&&i.push("domain="+a),!0===o&&i.push("secure"),document.cookie=i.join("; ")},read:function(e){e=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,n){"use strict";var r=n(20);function a(){this.handlers=[]}a.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},a.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},a.prototype.forEach=function(t){r.forEach(this.handlers,function(e){null!==e&&t(e)})},e.exports=a},function(e,t,n){"use strict";var r=n(20),a=n(465),o=n(287),i=n(90),s=n(466),l=n(467);function u(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(t){return u(t),t.baseURL&&!s(t.url)&&(t.url=l(t.baseURL,t.url)),t.headers=t.headers||{},t.data=a(t.data,t.headers,t.transformRequest),t.headers=r.merge(t.headers.common||{},t.headers[t.method]||{},t.headers||{}),r.forEach(["delete","get","head","post","put","patch","common"],function(e){delete t.headers[e]}),(t.adapter||i.adapter)(t).then(function(e){return u(t),e.data=a(e.data,e.headers,t.transformResponse),e},function(e){return o(e)||(u(t),e&&e.response&&(e.response.data=a(e.response.data,e.response.headers,t.transformResponse))),Promise.reject(e)})}},function(e,t,n){"use strict";var r=n(20);e.exports=function(t,n,e){return r.forEach(e,function(e){t=e(t,n)}),t}},function(e,t,n){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,n){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(288);function a(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new r(e),t(n.reason))})}a.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},a.source=function(){var t;return{token:new a(function(e){t=e}),cancel:t}},e.exports=a},function(e,t,n){"use strict";e.exports=function(t){return function(e){return t.apply(null,e)}}},function(e,t,n){"use strict";t.__esModule=!0,t.withContext=void 0;var h=d(n(3)),m=d(n(8)),o=d(n(4)),i=d(n(5)),r=d(n(6)),_=d(n(0)),l=d(n(11)),a=d(n(1)),y=d(n(27)),u=d(n(10)),s=n(7),g=d(n(289));function d(e){return e&&e.__esModule?e:{default:e}}var c,f=u.default.config,p=void 0,v={},r=(c=_.default.Component,(0,r.default)(b,c),b.prototype.componentWillUnmount=function(){var e,t=this.props.timeoutId;t in v&&(e=v[t],clearTimeout(e),delete v[t])},b.prototype.render=function(){var e=this.props,t=e.prefix,n=e.type,r=e.title,a=e.content,o=e.align,i=e.offset,s=e.hasMask,l=e.afterClose,u=e.animation,d=e.overlayProps,c=(e.timeoutId,e.className),f=e.style,p=(0,m.default)(e,["prefix","type","title","content","align","offset","hasMask","afterClose","animation","overlayProps","timeoutId","className","style"]),e=this.state.visible;return _.default.createElement(y.default,(0,h.default)({},d,{prefix:t,animation:u,visible:e,align:o,offset:i,hasMask:s,afterClose:l}),_.default.createElement(g.default,(0,h.default)({},p,{prefix:t,visible:!0,type:n,shape:"toast",title:r,style:f,className:t+"message-wrapper "+c,onClose:this.handleClose}),a))},r=n=b,n.contextTypes={prefix:a.default.string},n.propTypes={prefix:a.default.string,type:a.default.string,title:a.default.node,content:a.default.node,align:a.default.string,offset:a.default.array,hasMask:a.default.bool,afterClose:a.default.func,animation:a.default.oneOfType([a.default.object,a.default.bool]),overlayProps:a.default.object,onClose:a.default.func,timeoutId:a.default.string,style:a.default.object,className:a.default.string},n.defaultProps={prefix:"next-",align:"tc tc",offset:[0,30],hasMask:!1,animation:{in:"pulse",out:"zoomOut"},style:{},className:""},r);function b(){var e,t;(0,o.default)(this,b);for(var n=arguments.length,r=Array(n),a=0;a 16.8.0")},p.prototype.validate=function(e,t){this.validateCallback(e,t)},p.prototype.reset=function(e){var t=1e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=r.length)return n;var o=r[a],a=e(t&&t[o],n,r,a+1);if(!t){var i=isNaN(o)?{}:[];return i[o]=a,i}if(Array.isArray(t)){i=[].concat(t);return i[o]=a,i}return(0,l.default)({},t,(0,s.default)({},o,a))};t=function(){};void 0!==e&&e.env;n.warning=t}.call(this,a(284))},function(e,t,n){"use strict";t.__esModule=!0,t.cloneAndAddKey=function(e){if(e&&(0,a.isValidElement)(e)){var t=e.key||"error";return(0,a.cloneElement)(e,{key:t})}return e},t.scrollToFirstError=function(e){var t=e.errorsGroup,n=e.options,r=e.instance;if(t&&n.scrollToFirstError){var a,o=void 0,i=void 0;for(a in t)if(t.hasOwnProperty(a)){var s=u.default.findDOMNode(r[a]);if(!s)return;var l=s.offsetTop;(void 0===i||l), use ), use or
instead of.'),L.default.cloneElement(e,{className:n,size:c||S(a)})}return(0,k.isValidElement)(e)?e:L.default.createElement("span",{className:r+"btn-helper"},e)}),d=d,u=(0,b.default)({},T.obj.pickOthers(Object.keys(D.propTypes),y),{type:o,disabled:p,onClick:h,className:(0,x.default)(u)});return"button"!==d&&(delete u.type,u.disabled&&(delete u.onClick,u.href&&delete u.href)),L.default.createElement(d,(0,b.default)({},u,{dir:_?"rtl":void 0,onMouseUp:this.onMouseUp,ref:this.buttonRefHandler}),t,m)},r=n=D,n.propTypes=(0,b.default)({},s.default.propTypes,{prefix:a.default.string,rtl:a.default.bool,type:a.default.oneOf(["primary","secondary","normal"]),size:a.default.oneOf(["small","medium","large"]),icons:a.default.shape({loading:a.default.node}),iconSize:a.default.oneOfType([a.default.oneOf(["xxs","xs","small","medium","large","xl","xxl","xxxl","inherit"]),a.default.number]),htmlType:a.default.oneOf(["submit","reset","button"]),component:a.default.oneOf(["button","a","div","span"]),loading:a.default.bool,ghost:a.default.oneOf([!0,!1,"light","dark"]),text:a.default.bool,warning:a.default.bool,disabled:a.default.bool,onClick:a.default.func,className:a.default.string,onMouseUp:a.default.func,children:a.default.node}),n.defaultProps={prefix:"next-",type:"normal",size:"medium",icons:{},htmlType:"button",component:"button",loading:!1,ghost:!1,text:!1,warning:!1,disabled:!1,onClick:function(){}},r);function D(){var e,t;(0,o.default)(this,D);for(var n=arguments.length,r=Array(n),a=0;as&&!u&&(e=e.slice(0,s),r=S.default.createElement(m.default,{key:"_count",type:"primary",size:c,animation:!1},l(f,p))),0x',"Tag"),"readonly"!==n&&"interactive"!==n||a.log.warning("Warning: [ shape="+n+" ] is deprecated at [ Tag ]"),"secondary"===r&&a.log.warning("Warning: [ type=secondary ] is deprecated at [ Tag ]"),["count","marked","value","onChange"].forEach(function(e){e in t&&a.log.warning("Warning: [ "+e+" ] is deprecated at [ Tag ]")}),("selected"in t||"defaultSelected"in t)&&a.log.warning("Warning: [ selected|defaultSelected ] is deprecated at [ Tag ], use [ checked|defaultChecked ] at [ Tag.Selectable ] instead of it"),"closed"in t&&a.log.warning("Warning: [ closed ] is deprecated at [ Tag ], use [ onClose ] at [ Tag.Closeable ] instead of it"),"onSelect"in t&&e("onSelect","","Tag"),"afterClose"in t&&a.log.warning("Warning: [ afterClose ] is deprecated at [ Tag ], use [ afterClose ] at [ Tag.Closeable ] instead of it"),t}});o.Group=r.default.config(i.default),o.Selectable=r.default.config(s.default),o.Closable=r.default.config(n.default),o.Closeable=o.Closable,t.default=o,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var o=f(n(3)),i=f(n(8)),r=f(n(4)),a=f(n(5)),s=f(n(6)),l=n(0),u=f(l),d=f(n(1)),c=f(n(9));function f(e){return e&&e.__esModule?e:{default:e}}var p,s=(p=l.Component,(0,s.default)(h,p),h.prototype.render=function(){var e=this.props,t=e.className,n=e.prefix,r=e.children,a=e.rtl,e=(0,i.default)(e,["className","prefix","children","rtl"]),t=(0,c.default)((n||"next-")+"tag-group",t);return u.default.createElement("div",(0,o.default)({className:t,dir:a?"rtl":void 0},e),r)},s=l=h,l.propTypes={prefix:d.default.string,className:d.default.any,children:d.default.node,rtl:d.default.bool},l.defaultProps={prefix:"next-",rtl:!1},s);function h(){return(0,r.default)(this,h),(0,a.default)(this,p.apply(this,arguments))}s.displayName="Group",t.default=s,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var r=h(n(3)),a=h(n(4)),o=h(n(5)),i=h(n(6)),s=n(0),l=h(s),u=h(n(1)),d=h(n(9)),c=n(13),f=n(7),p=h(n(94));function h(e){return e&&e.__esModule?e:{default:e}}var m,n=f.func.noop,_=f.func.bindCtx,i=(m=s.Component,(0,i.default)(y,m),y.getDerivedStateFromProps=function(e,t){return void 0!==e.checked&&e.checked!==t.checked?{checked:e.checked}:null},y.prototype.handleClick=function(e){if(e&&e.preventDefault(),this.props.disabled)return!1;var t=this.state.checked;this.setState({checked:!t}),this.props.onChange(!t,e)},y.prototype.render=function(){var e=f.obj.pickOthers(["checked","defaultChecked","onChange","className","_shape","closable"],this.props),t=("checked"in this.props?this.props:this.state).checked,n=(0,d.default)(this.props.className,{checked:t});return l.default.createElement(p.default,(0,r.default)({},e,{role:"checkbox",_shape:"checkable","aria-checked":t,className:n,onClick:this.handleClick}))},i=s=y,s.propTypes={checked:u.default.bool,defaultChecked:u.default.bool,onChange:u.default.func,disabled:u.default.bool,className:u.default.any},s.defaultProps={onChange:n},i);function y(e){(0,a.default)(this,y);var t=(0,o.default)(this,m.call(this,e));return t.state={checked:"checked"in e?e.checked:e.defaultChecked||!1},_(t,["handleClick"]),t}i.displayName="Selectable",t.default=(0,c.polyfill)(i),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var c=l(n(3)),f=l(n(8)),r=l(n(4)),a=l(n(5)),o=l(n(6)),i=n(0),p=l(i),s=l(n(1)),h=l(n(94));function l(e){return e&&e.__esModule?e:{default:e}}var u,o=(u=i.Component,(0,o.default)(d,u),d.prototype.render=function(){var e=this.props,t=e.disabled,n=e.className,r=e.closeArea,a=e.onClose,o=e.afterClose,i=e.onClick,s=e.type,l=e.size,u=e.children,d=e.rtl,e=(0,f.default)(e,["disabled","className","closeArea","onClose","afterClose","onClick","type","size","children","rtl"]);return p.default.createElement(h.default,(0,c.default)({},e,{rtl:d,disabled:t,className:n,closeArea:r,onClose:a,afterClose:o,onClick:i,type:s,size:l,closable:!0}),u)},o=i=d,i.propTypes={disabled:s.default.bool,className:s.default.any,closeArea:s.default.oneOf(["tag","tail"]),onClose:s.default.func,afterClose:s.default.func,onClick:s.default.func,type:s.default.oneOf(["normal","primary"]),size:s.default.oneOf(["small","medium","large"]),children:s.default.any,rtl:s.default.bool},i.defaultProps={disabled:!1,type:"normal"},o);function d(){return(0,r.default)(this,d),(0,a.default)(this,u.apply(this,arguments))}o.displayName="Closeable",t.default=o,e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var a=f(n(3)),o=f(n(8)),i=f(n(4)),s=f(n(5)),r=f(n(6)),l=f(n(0)),u=f(n(1)),d=f(n(297)),c=f(n(14));function f(e){return e&&e.__esModule?e:{default:e}}function p(e){e.preventDefault()}var h,r=(h=d.default,(0,r.default)(m,h),m.prototype.render=function(){var e=this.props,t=e.showToggle,n=(0,o.default)(e,["showToggle"]),r=this.state,e=r.hint,r=r.htmlType,e=t?l.default.createElement(c.default,{type:e,onClick:this.toggleEye,onMouseDown:p}):null;return l.default.createElement(d.default,(0,a.default)({},n,{extra:e,htmlType:r}))},r=n=m,n.getDerivedStateFromProps=d.default.getDerivedStateFromProps,n.propTypes=(0,a.default)({},d.default.propTypes,{showToggle:u.default.bool}),n.defaultProps=(0,a.default)({},d.default.defaultProps,{showToggle:!0}),r);function m(){var e,t;(0,i.default)(this,m);for(var n=arguments.length,r=Array(n),a=0;a, as child."),r.push(t),e.props.children&&(t.children=n(e.props.children)))}),r}(e.children):t},O.prototype.fetchInfoFromBinaryChildren=function(e){function o(e,t){return t=t||0,e.forEach(function(e){e.children?t=o(e.children,t):t+=1}),t}var r=!1,a=[],i=[];(function t(){var e=0e.clientHeight,e.scrollWidth,e.clientWidth,(e={})[a]=n,e=e,i||(e[r]=n),t||(e[r]=0,e[a]=0),+n&&(e.marginBottom=-n,e.paddingBottom=n,t&&(e[a]=n)),m.dom.setStyle(this.headerNode,e))},o.prototype.render=function(){var e=this.props,t=e.components,n=e.className,r=e.prefix,a=e.fixedHeader,o=e.lockType,i=e.dataSource,e=(e.maxBodyHeight,(0,u.default)(e,["components","className","prefix","fixedHeader","lockType","dataSource","maxBodyHeight"]));return a&&((t=(0,l.default)({},t)).Header||(t.Header=_.default),t.Body||(t.Body=y.default),t.Wrapper||(t.Wrapper=g.default),n=(0,h.default)(((a={})[r+"table-fixed"]=!0,a[r+"table-wrap-empty"]=!i.length,a[n]=n,a))),f.default.createElement(s,(0,l.default)({},e,{dataSource:i,lockType:o,components:t,className:n,prefix:r}))},o}(f.default.Component),e.FixedHeader=_.default,e.FixedBody=y.default,e.FixedWrapper=g.default,e.propTypes=(0,l.default)({hasHeader:a.default.bool,fixedHeader:a.default.bool,maxBodyHeight:a.default.oneOfType([a.default.number,a.default.string])},s.propTypes),e.defaultProps=(0,l.default)({},s.defaultProps,{hasHeader:!0,fixedHeader:!1,maxBodyHeight:200,components:{},refs:{},prefix:"next-"}),e.childContextTypes={fixedHeader:a.default.bool,getNode:a.default.func,onFixedScrollSync:a.default.func,getTableInstanceForFixed:a.default.func,maxBodyHeight:a.default.oneOfType([a.default.number,a.default.string])},t);return t.displayName="FixedTable",(0,o.statics)(t,s),t};var f=i(n(0)),a=i(n(1)),p=n(11),h=i(n(9)),m=n(7),_=i(n(64)),y=i(n(308)),g=i(n(65)),o=n(25);function i(e){return e&&e.__esModule?e:{default:e}}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var i=o(n(8)),f=o(n(3)),a=o(n(4)),s=o(n(5)),l=o(n(6));t.default=function(o){var e,t=(t=e=function(n){function r(e,t){(0,a.default)(this,r);var c=(0,s.default)(this,n.call(this,e,t));return c.addSelection=function(e){var t=c.props,n=t.prefix,r=t.rowSelection,t=t.size,r=r.columnProps&&r.columnProps()||{};e.find(function(e){return"selection"===e.key})||e.unshift((0,f.default)({key:"selection",title:c.renderSelectionHeader.bind(c),cell:c.renderSelectionBody.bind(c),width:"small"===t?34:50,className:n+"table-selection "+n+"table-prerow",__normalized:!0},r))},c.renderSelectionHeader=function(){var e=c.selectAllRow,t={},n=c.props,r=n.rowSelection,a=n.primaryKey,o=n.dataSource,i=n.entireDataSource,s=n.locale,l=c.state.selectedRowKeys,n=r.mode||"multiple",u=!!l.length,d=!1;c.flatDataSource(i||o).filter(function(e,t){return!r.getProps||!(r.getProps(e,t)||{}).disabled}).map(function(e){return e[a]}).forEach(function(e){-1===l.indexOf(e)?u=!1:d=!0}),t.onClick=b(function(e){e.stopPropagation()},t.onClick);o=r.titleProps&&r.titleProps()||{};return u&&(d=!1),["multiple"===n?p.default.createElement(h.default,(0,f.default)({key:"_total",indeterminate:d,"aria-label":s.selectAll,checked:u,onChange:e},t,o)):null,r.titleAddons&&r.titleAddons()]},c.renderSelectionBody=function(e,t,n){var r=c.props,a=r.rowSelection,o=r.primaryKey,i=c.state.selectedRowKeys,r=a.mode||"multiple",i=-1c.clientHeight;this.isLock()?(e=this.bodyLeftNode,t=this.bodyRightNode,r=this.getWrapperNode("right"),n=f?u:0,c=c.offsetHeight-u,f||(d[s]=0,d[l]=0),+u?(d.marginBottom=-u,d.paddingBottom=u):(d.marginBottom=-20,d.paddingBottom=20),c={"max-height":c},o||+u||(c[l]=0),+u&&(c[l]=-u),e&&_.dom.setStyle(e,c),t&&_.dom.setStyle(t,c),r&&+u&&_.dom.setStyle(r,a?"left":"right",n+"px")):(d.marginBottom=-u,d.paddingBottom=u,d[l]=0,f||(d[s]=0)),i&&_.dom.setStyle(i,d)},r.prototype.adjustHeaderSize=function(){var o=this;this.isLock()&&this.tableInc.groupChildren.forEach(function(e,t){var n=o.tableInc.groupChildren[t].length-1,r=o.getHeaderCellNode(t,n),a=o.getHeaderCellNode(t,0),n=o.getHeaderCellNode(t,0,"right"),t=o.getHeaderCellNode(t,0,"left");r&&n&&(r=r.offsetHeight,_.dom.setStyle(n,"height",r),setTimeout(function(){var e=o.tableRightInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()})),a&&t&&(a=a.offsetHeight,_.dom.setStyle(t,"height",a),setTimeout(function(){var e=o.tableLeftInc.affixRef;return e&&e.getInstance()&&e.getInstance().updatePosition()}))})},r.prototype.adjustRowHeight=function(){var n=this;this.isLock()&&this.tableInc.props.dataSource.forEach(function(e,t){e=("object"===(void 0===e?"undefined":(0,a.default)(e))&&"__rowIndex"in e?e.__rowIndex:t)+(e.__expanded?"_expanded":"");n.setRowHeight(e,"left"),n.setRowHeight(e,"right")})},r.prototype.setRowHeight=function(e,t){var n=this.getRowNode(e,t),t=this.getRowNode(e),e=(w?t&&t.offsetHeight:t&&parseFloat(getComputedStyle(t).height))||"auto",t=(w?n&&n.offsetHeight:n&&parseFloat(getComputedStyle(n).height))||"auto";n&&e!==t&&_.dom.setStyle(n,"height",e)},r.prototype.getWrapperNode=function(e){e=e?e.charAt(0).toUpperCase()+e.substr(1):"";try{return(0,u.findDOMNode)(this["lock"+e+"El"])}catch(e){return null}},r.prototype.getRowNode=function(e,t){t=this["table"+(t=t?t.charAt(0).toUpperCase()+t.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(t.getRowRef(e))}catch(e){return null}},r.prototype.getHeaderCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getHeaderCellRef(e,t))}catch(e){return null}},r.prototype.getCellNode=function(e,t,n){n=this["table"+(n=n?n.charAt(0).toUpperCase()+n.substr(1):"")+"Inc"];try{return(0,u.findDOMNode)(n.getCellRef(e,t))}catch(e){return null}},r.prototype.render=function(){var e=this.props,t=(e.children,e.columns,e.prefix),n=e.components,r=e.className,a=e.dataSource,o=e.tableWidth,i=(0,f.default)(e,["children","columns","prefix","components","className","dataSource","tableWidth"]),s=this.normalizeChildrenState(this.props),l=s.lockLeftChildren,u=s.lockRightChildren,d=s.children,e={left:this.getFlatenChildrenLength(l),right:this.getFlatenChildrenLength(u),origin:this.getFlatenChildrenLength(d)};if(this._notNeedAdjustLockLeft&&(l=[]),this._notNeedAdjustLockRight&&(u=[]),this.lockLeftChildren=l,this.lockRightChildren=u,this.isOriginLock()){(n=(0,p.default)({},n)).Body=n.Body||g.default,n.Header=n.Header||v.default,n.Wrapper=n.Wrapper||b.default,n.Row=n.Row||y.default;r=(0,m.default)(((s={})[t+"table-lock"]=!0,s[t+"table-wrap-empty"]=!a.length,s[r]=r,s)),u=[h.default.createElement(c,(0,p.default)({},i,{dataSource:a,key:"lock-left",columns:l,className:t+"table-lock-left",lengths:e,prefix:t,lockType:"left",components:n,ref:this.saveLockLeftRef,loading:!1,"aria-hidden":!0})),h.default.createElement(c,(0,p.default)({},i,{dataSource:a,key:"lock-right",columns:u,className:t+"table-lock-right",lengths:e,prefix:t,lockType:"right",components:n,ref:this.saveLockRightRef,loading:!1,"aria-hidden":!0}))];return h.default.createElement(c,(0,p.default)({},i,{tableWidth:o,dataSource:a,columns:d,prefix:t,lengths:e,wrapperContent:u,components:n,className:r}))}return h.default.createElement(c,this.props)},r}(h.default.Component),e.LockRow=y.default,e.LockBody=g.default,e.LockHeader=v.default,e.propTypes=(0,p.default)({scrollToCol:r.default.number,scrollToRow:r.default.number},c.propTypes),e.defaultProps=(0,p.default)({},c.defaultProps),e.childContextTypes={getTableInstance:r.default.func,getLockNode:r.default.func,onLockBodyScroll:r.default.func,onRowMouseEnter:r.default.func,onRowMouseLeave:r.default.func},t);return t.displayName="LockTable",(0,M.statics)(t,c),t};var l=n(0),h=c(l),u=n(11),r=c(n(1)),m=c(n(9)),d=c(n(96)),_=n(7),y=c(n(98)),g=c(n(309)),v=c(n(310)),b=c(n(65)),M=n(25);function c(e){return e&&e.__esModule?e:{default:e}}var w=_.env.ieVersion;function k(e){return function n(e){return e.map(function(e){var t=(0,p.default)({},e);return e.children&&(e.children=n(e.children)),t})}(e)}e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var l=s(n(8)),u=s(n(3)),a=s(n(4)),o=s(n(5)),i=s(n(6));t.default=function(s){var e,t=(t=e=function(n){function r(e,t){(0,a.default)(this,r);var d=(0,o.default)(this,n.call(this,e));return d.state={},d.updateOffsetArr=function(){var e=d.splitChildren||{},t=e.lockLeftChildren,n=e.lockRightChildren,r=e.originChildren,a=d.getFlatenChildren(t).length,e=d.getFlatenChildren(n).length,r=a+e+d.getFlatenChildren(r).length,a=0i.top-e.offset?(r?(a.position="absolute",a.top=o-(i.top-e.offset),n.position="relative"):(a.position="fixed",a.top=e.offset+t.top),u._setAffixStyle(a,!0),u._setContainerStyle(n)):e.bottom&&on&&(t.current=n),this.setState(t),this.props.onPageSizeChange(e)},H.prototype.renderPageTotal=function(){var e=this.props,t=e.prefix,n=e.total,r=e.totalRender,a=this.state,e=a.currentPageSize,a=a.current;return O.default.createElement("div",{className:t+"pagination-total"},r(n,[(a-1)*e+1,a*e]))},H.prototype.renderPageItem=function(e){var t=this.props,n=t.prefix,r=t.size,a=t.link,o=t.pageNumberRender,i=t.total,s=t.pageSize,l=t.locale,t=this.state.current,s=j(i,s),t=parseInt(e,10)===t,t={size:r,className:(0,N.default)(((r={})[n+"pagination-item"]=!0,r[n+"current"]=t,r)),onClick:t?m:this.onPageItemClick.bind(this,e)};return a&&(t.component="a",t.href=a.replace("{page}",e)),O.default.createElement(c.default,(0,E.default)({"aria-label":P.str.template(l.total,{current:e,total:s})},t,{key:e}),o(e))},H.prototype.renderPageFirst=function(e){var t=this.props,n=t.prefix,r=t.size,a=t.shape,t=t.locale,r={disabled:e<=1,size:r,className:(0,N.default)(((r={})[n+"pagination-item"]=!0,r[n+"prev"]=!0,r)),onClick:this.onPageItemClick.bind(this,e-1)},n=O.default.createElement(d.default,{type:"arrow-left",className:n+"pagination-icon-prev"});return O.default.createElement(c.default,(0,E.default)({},r,{"aria-label":P.str.template(t.labelPrev,{current:e})}),n,"arrow-only"===a||"arrow-prev-only"===a||"no-border"===a?"":t.prev)},H.prototype.renderPageLast=function(e,t){var n=this.props,r=n.prefix,a=n.size,o=n.shape,n=n.locale,a={disabled:t<=e,size:a,className:(0,N.default)(((a={})[r+"pagination-item"]=!0,a[r+"next"]=!0,a)),onClick:this.onPageItemClick.bind(this,e+1)},r=O.default.createElement(d.default,{type:"arrow-right",className:r+"pagination-icon-next"});return O.default.createElement(c.default,(0,E.default)({},a,{"aria-label":P.str.template(n.labelNext,{current:e})}),"arrow-only"===o||"no-border"===o?"":n.next,r)},H.prototype.renderPageEllipsis=function(e){var t=this.props.prefix;return O.default.createElement(d.default,{className:t+"pagination-ellipsis "+t+"pagination-icon-ellipsis",type:"ellipsis",key:"ellipsis-"+e})},H.prototype.renderPageJump=function(){var t=this,e=this.props,n=e.prefix,r=e.size,a=e.locale,e=this.state.inputValue;return[O.default.createElement("span",{className:n+"pagination-jump-text"},a.goTo),O.default.createElement(f.default,{className:n+"pagination-jump-input",type:"text","aria-label":a.inputAriaLabel,size:r,value:e,onChange:this.onInputChange.bind(this),onKeyDown:function(e){e.keyCode===P.KEYCODE.ENTER&&t.handleJump(e)}}),O.default.createElement("span",{className:n+"pagination-jump-text"},a.page),O.default.createElement(c.default,{className:n+"pagination-jump-go",size:r,onClick:this.handleJump},a.go)]},H.prototype.renderPageDisplay=function(e,t){var n=this.props,r=n.prefix,n=n.pageNumberRender;return O.default.createElement("span",{className:r+"pagination-display"},O.default.createElement("em",null,n(e)),"/",n(t))},H.prototype.renderPageList=function(e,t){var n=this.props,r=n.prefix,a=n.pageShowCount,o=[];if(t<=a)for(var i=1;i<=t;i++)o.push(this.renderPageItem(i));else{var s=a-3,n=parseInt(s/2,10),a=void 0,l=void 0;o.push(this.renderPageItem(1)),l=e+n,(a=e-n)<=1&&(l=(a=2)+s),2, or explicitly pass "'+d+'" as a prop to "'+a+'".'),n.initSelector(),n.initSubscription(),n}P(e,r);var t=e.prototype;return t.getChildContext=function(){var e=this.propsMode?null:this.subscription,t={};return t[p]=e||this.context[p],t},t.componentDidMount=function(){u&&(this.subscription.trySubscribe(),this.selector.run(this.props),this.selector.shouldComponentUpdate&&this.forceUpdate())},t.componentWillReceiveProps=function(e){this.selector.run(e)},t.shouldComponentUpdate=function(){return this.selector.shouldComponentUpdate},t.componentWillUnmount=function(){this.subscription&&this.subscription.tryUnsubscribe(),this.subscription=null,this.notifyNestedSubs=re,this.store=null,this.selector.run=re,this.selector.shouldComponentUpdate=!1},t.getWrappedInstance=function(){return q()(c,"To access the wrapped instance, you need to specify { withRef: true } in the options argument of the "+s+"() call."),this.wrappedInstance},t.setWrappedInstance=function(e){this.wrappedInstance=e},t.initSelector=function(){var n,r,a,e=i(this.store.dispatch,o);this.selector=(n=e,r=this.store,a={run:function(e){try{var t=n(r.getState(),e);t===a.props&&!a.error||(a.shouldComponentUpdate=!0,a.props=t,a.error=null)}catch(e){a.shouldComponentUpdate=!0,a.error=e}}}),this.selector.run(this.props)},t.initSubscription=function(){var e;u&&(e=(this.propsMode?this.props:this.context)[p],this.subscription=new X(this.store,e,this.onStateChange.bind(this)),this.notifyNestedSubs=this.subscription.notifyNestedSubs.bind(this.subscription))},t.onStateChange=function(){this.selector.run(this.props),this.selector.shouldComponentUpdate?(this.componentDidUpdate=this.notifyNestedSubsOnComponentDidUpdate,this.setState(ne)):this.notifyNestedSubs()},t.notifyNestedSubsOnComponentDidUpdate=function(){this.componentDidUpdate=void 0,this.notifyNestedSubs()},t.isSubscribed=function(){return Boolean(this.subscription)&&this.subscription.isSubscribed()},t.addExtraProps=function(e){if(!(c||l||this.propsMode&&this.subscription))return e;e=U({},e);return c&&(e.ref=this.setWrappedInstance),l&&(e[l]=this.renderCount++),this.propsMode&&this.subscription&&(e[p]=this.subscription),e},t.render=function(){var e=this.selector;if(e.shouldComponentUpdate=!1,e.error)throw e.error;return Object(y.createElement)(n,this.addExtraProps(e.props))},e}(y.Component);return ee&&(e.prototype.UNSAFE_componentWillReceiveProps=e.prototype.componentWillReceiveProps,delete e.prototype.componentWillReceiveProps),e.WrappedComponent=n,e.displayName=a,e.childContextTypes=_,e.contextTypes=m,e.propTypes=m,$()(e,n)}}var oe=Object.prototype.hasOwnProperty;function ie(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function se(e,t){if(ie(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var a=0;a outside a "),this.isStatic()&&this.perform()},tt.prototype.componentDidMount=function(){this.isStatic()||this.perform()},tt.prototype.componentDidUpdate=function(e){var t=Re(e.to),n=Re(this.props.to);e=n,(t=t).pathname===e.pathname&&t.search===e.search&&t.hash===e.hash&&t.key===e.key&&Ce(t.state,e.state)?xe()(!1,"You tried to redirect to the same route you're currently on: \""+n.pathname+n.search+'"'):this.perform()},tt.prototype.computeTo=function(e){var t=e.computedMatch,e=e.to;return t?"string"==typeof e?Ze(e,t.params):Xe({},e,{pathname:Ze(e.pathname,t.params)}):e},tt.prototype.perform=function(){var e=this.context.router.history,t=this.props.push,n=this.computeTo(this.props);t?e.push(n):e.replace(n)},tt.prototype.render=function(){return null},tt);function tt(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,tt),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,et.apply(this,arguments))}E.propTypes={computedMatch:H.a.object,push:H.a.bool,from:H.a.string,to:H.a.oneOfType([H.a.string,H.a.object]).isRequired},E.defaultProps={push:!1},E.contextTypes={router:H.a.shape({history:H.a.shape({push:H.a.func.isRequired,replace:H.a.func.isRequired}).isRequired,staticContext:H.a.object}).isRequired};var nt=E,rt=Object.assign||function(e){for(var t=1;t may have only one child element"),this.unlisten=r.listen(function(){e.setState({match:e.computeMatch(r.location.pathname)})})},it.prototype.componentWillReceiveProps=function(e){xe()(this.props.history===e.history,"You cannot change ")},it.prototype.componentWillUnmount=function(){this.unlisten()},it.prototype.render=function(){var e=this.props.children;return e?_.a.Children.only(e):null},it);function it(){var e,t;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,it);for(var n=arguments.length,r=Array(n),a=0;a ignores the history prop. To use a custom history, use `import { Router }` instead of `import { HashRouter as Router }`.")},dt.prototype.render=function(){return _.a.createElement(st,{history:this.history,children:this.props.children})},dt);function dt(){var e,t;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,dt);for(var n=arguments.length,r=Array(n),a=0;a outside a ")},yt.prototype.componentWillReceiveProps=function(e){xe()(!(e.location&&!this.props.location),' elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'),xe()(!(!e.location&&this.props.location),' elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.')},yt.prototype.render=function(){var i=this.context.router.route,e=this.props.children,s=this.props.location||i.location,l=void 0,u=void 0;return _.a.Children.forEach(e,function(e){var t,n,r,a,o;null==l&&_.a.isValidElement(e)&&(t=(o=e.props).path,n=o.exact,r=o.strict,a=o.sensitive,o=o.from,u=e,l=mt(s.pathname,{path:t||o,exact:n,strict:r,sensitive:a},i.match))}),l?_.a.cloneElement(u,{location:s,computedMatch:l}):null},yt);function yt(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,yt),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,_t.apply(this,arguments))}J.contextTypes={router:H.a.shape({route:H.a.object.isRequired}).isRequired},J.propTypes={children:H.a.node,location:H.a.object};var gt=J,vt=Object.assign||function(e){for(var t=1;t or withRouter() outside a ");t=t.route,r=(r||t.location).pathname;return mt(r,{path:a,strict:o,exact:i,sensitive:e},t.match)},kt.prototype.componentWillMount=function(){xe()(!(this.props.component&&this.props.render),"You should not use and in the same route; will be ignored"),xe()(!(this.props.component&&this.props.children&&!Mt(this.props.children)),"You should not use and in the same route; will be ignored"),xe()(!(this.props.render&&this.props.children&&!Mt(this.props.children)),"You should not use and in the same route; will be ignored")},kt.prototype.componentWillReceiveProps=function(e,t){xe()(!(e.location&&!this.props.location),' elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'),xe()(!(!e.location&&this.props.location),' elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'),this.setState({match:this.computeMatch(e,t.router)})},kt.prototype.render=function(){var e=this.state.match,t=this.props,n=t.children,r=t.component,a=t.render,o=this.context.router,i=o.history,t=o.route,o=o.staticContext,o={match:e,location:this.props.location||t.location,history:i,staticContext:o};return r?e?_.a.createElement(r,o):null:a?e?a(o):null:"function"==typeof n?n(o):n&&!Mt(n)?_.a.Children.only(n):null},kt);function kt(){var e,t;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,kt);for(var n=arguments.length,r=Array(n),a=0;a
pom
- 0.4.9
+ 0.4.9-batchRegister
2.5.14
2020.0.2
UTF-8
3.4.9
4.1.0
1.10.19
- 1.4.2
- 1.18.2
+ 2.3.2
+ 1.18.30
3.0.0
1.3.1
3.12.0