diff --git a/README.md b/README.md index 013bdf2a..604cb5e1 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.5.0.tar.gz ``` @@ -157,4 +157,16 @@ sh startup.sh start http://127.0.0.1:8083/#/serviceSync -``` \ No newline at end of file +``` + +## Advanced Configuration + +### Full Synchronization from Zookeeper to Nacos (Dubbo) +When “*” is entered in the “Service Name” field of this form, it will fully synchronize all services from Zookeeper to Nacos, but only when using Dubbo. +![img_1.png](img_1.png) + +### Full Synchronization from Nacos to Nacos +When “All” is entered in the “Service Name” field of this form, it will automatically synchronize all registered services within the **default group** of the current cluster. + +This description explains the functionality clearly for English-speaking users. +![img_2.png](img_2.png) \ No newline at end of file diff --git a/img_1.png b/img_1.png new file mode 100644 index 00000000..eee07444 Binary files /dev/null and b/img_1.png differ diff --git a/img_2.png b/img_2.png new file mode 100644 index 00000000..50dcc2cd Binary files /dev/null and b/img_2.png differ diff --git a/nacossync-worker/pom.xml b/nacossync-worker/pom.xml index 2a553059..96a8cadf 100644 --- a/nacossync-worker/pom.xml +++ b/nacossync-worker/pom.xml @@ -59,17 +59,12 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j - - io.springfox - springfox-swagger2 - - - io.springfox - springfox-swagger-ui + org.springdoc + springdoc-openapi-ui org.springframework.boot @@ -169,8 +164,8 @@ org.apache.maven.plugins maven-compiler-plugin - 9 - 9 + 11 + 11 diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/NacosSyncMain.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/NacosSyncMain.java index 19e44a14..b2ea8c8a 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/NacosSyncMain.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/NacosSyncMain.java @@ -19,15 +19,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; - import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; -import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @author NacosSync * @version $Id: SkyWalkerMain.java, v 0.1 2018-09-24 PM12:42 NacosSync Exp $$ */ -@EnableSwagger2 @SpringBootApplication(exclude = EurekaClientAutoConfiguration.class) public class NacosSyncMain { diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/cache/SkyWalkerCacheServices.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/cache/SkyWalkerCacheServices.java index 7f4a40fd..b515bf74 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/cache/SkyWalkerCacheServices.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/cache/SkyWalkerCacheServices.java @@ -25,16 +25,17 @@ import com.alibaba.nacossync.pojo.model.TaskDO; import com.alibaba.nacossync.util.SkyWalkerUtil; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + /** * @author NacosSync * @version $Id: SkyWalkerCacheServices.java, v 0.1 2018-09-27 AM2:47 NacosSync Exp $$ @@ -48,7 +49,7 @@ public class SkyWalkerCacheServices { @Autowired private ObjectMapper objectMapper; - private static Map finishedTaskMap = new ConcurrentHashMap<>(); + private static final Map finishedTaskMap = new ConcurrentHashMap<>(); public String getClusterConnectKey(String clusterId) { List allClusterConnectKey = getAllClusterConnectKey(clusterId); @@ -90,7 +91,7 @@ public FinishedTask getFinishedTask(TaskDO taskDO) { String operationId = SkyWalkerUtil.getOperationId(taskDO); - if (StringUtils.isEmpty(operationId)) { + if (StringUtils.hasLength(operationId)) { return null; } @@ -98,14 +99,14 @@ public FinishedTask getFinishedTask(TaskDO taskDO) { } public FinishedTask getFinishedTask(String operationId) { - if (StringUtils.isEmpty(operationId)) { + if (StringUtils.hasLength(operationId)) { return null; } return finishedTaskMap.get(operationId); } public FinishedTask removeFinishedTask(String operationId) { - if (StringUtils.isEmpty(operationId)) { + if (StringUtils.hasLength(operationId)) { return null; } return finishedTaskMap.remove(operationId); diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/constant/SkyWalkerConstants.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/constant/SkyWalkerConstants.java index 0379e73e..6b85af87 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/constant/SkyWalkerConstants.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/constant/SkyWalkerConstants.java @@ -36,6 +36,6 @@ public class SkyWalkerConstants { public static final String PAGE_NO="pageNo"; public static final String PAGE_SIZE="pageSize"; public static final String SYNC_INSTANCE_TAG="sync.instance.tag"; - public static final Integer MAX_THREAD_NUM = 200; + public static final String NACOS_ALL_SERVICE_NAME = "ALL"; } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/TaskAccessService.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/TaskAccessService.java index 649eaf41..8dc3d5e6 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/TaskAccessService.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/TaskAccessService.java @@ -16,6 +16,7 @@ */ package com.alibaba.nacossync.dao; +import com.alibaba.nacossync.constant.SkyWalkerConstants; import com.alibaba.nacossync.pojo.QueryCondition; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -60,7 +61,7 @@ public void deleteTaskById(String taskId) { */ public void deleteTaskInBatch(List taskIds) { List tds=taskRepository.findAllByTaskIdIn(taskIds); - taskRepository.deleteInBatch(tds); + taskRepository.deleteAllInBatch(tds); } public Iterable findAll() { @@ -115,8 +116,12 @@ private Page getTaskDOS(QueryCondition queryCondition, Pageable pageable }, pageable); } - public List findServiceNameIsNull() { - return taskRepository.findAllByServiceNameEquals("ALL"); + public List findAllByServiceNameEqualAll() { + return taskRepository.findAllByServiceNameEqualsIgnoreCase(SkyWalkerConstants.NACOS_ALL_SERVICE_NAME); + } + + public List findAllByServiceNameNotEqualAll() { + return taskRepository.findAllByServiceNameNotIgnoreCase(SkyWalkerConstants.NACOS_ALL_SERVICE_NAME); } } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/repository/TaskRepository.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/repository/TaskRepository.java index fed44c45..2b40a8ce 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/repository/TaskRepository.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/dao/repository/TaskRepository.java @@ -16,15 +16,13 @@ */ package com.alibaba.nacossync.dao.repository; -import java.util.List; - -import javax.transaction.Transactional; - +import com.alibaba.nacossync.pojo.model.TaskDO; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; -import com.alibaba.nacossync.pojo.model.TaskDO; +import javax.transaction.Transactional; +import java.util.List; /** * @author NacosSync @@ -47,6 +45,7 @@ public interface TaskRepository extends CrudRepository, JpaRepo * @param serviceName * @return */ - List findAllByServiceNameEquals(String serviceName); + List findAllByServiceNameEqualsIgnoreCase(String serviceName); + List findAllByServiceNameNotIgnoreCase(String serviceName); } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/event/DeleteAllSubTaskEvent.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/event/DeleteAllSubTaskEvent.java new file mode 100644 index 00000000..05b2eba4 --- /dev/null +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/event/DeleteAllSubTaskEvent.java @@ -0,0 +1,13 @@ +package com.alibaba.nacossync.event; + +import com.alibaba.nacossync.pojo.model.TaskDO; +import lombok.Data; + +@Data +public class DeleteAllSubTaskEvent { + public DeleteAllSubTaskEvent(TaskDO taskDO) { + this.taskDO = taskDO; + } + + private final TaskDO taskDO; +} diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/event/listener/EventListener.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/event/listener/EventListener.java index 40b3c12a..c15ff5f9 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/event/listener/EventListener.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/event/listener/EventListener.java @@ -16,21 +16,19 @@ */ package com.alibaba.nacossync.event.listener; -import javax.annotation.PostConstruct; - -import com.alibaba.nacossync.constant.MetricsStatisticsType; -import com.alibaba.nacossync.monitor.MetricsManager; -import lombok.extern.slf4j.Slf4j; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import com.alibaba.nacossync.cache.SkyWalkerCacheServices; +import com.alibaba.nacossync.constant.MetricsStatisticsType; import com.alibaba.nacossync.event.DeleteTaskEvent; import com.alibaba.nacossync.event.SyncTaskEvent; import com.alibaba.nacossync.extension.SyncManagerService; +import com.alibaba.nacossync.monitor.MetricsManager; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; /** * @author NacosSync @@ -80,7 +78,7 @@ public void listenerDeleteTaskEvent(DeleteTaskEvent deleteTaskEvent) { try { long start = System.currentTimeMillis(); if (syncManagerService.delete(deleteTaskEvent.getTaskDO())) { - skyWalkerCacheServices.addFinishedTask(deleteTaskEvent.getTaskDO()); + skyWalkerCacheServices.removeFinishedTask(deleteTaskEvent.getTaskDO().getOperationId()); metricsManager.record(MetricsStatisticsType.DELETE_TASK_RT, System.currentTimeMillis() - start); } else { log.warn("listenerDeleteTaskEvent delete failure"); diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/SyncManagerService.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/SyncManagerService.java index 252ee6e9..2fd33d18 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/SyncManagerService.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/SyncManagerService.java @@ -12,14 +12,12 @@ */ package com.alibaba.nacossync.extension; -import static com.alibaba.nacossync.util.SkyWalkerUtil.generateSyncKey; - import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacossync.cache.SkyWalkerCacheServices; import com.alibaba.nacossync.constant.ClusterTypeEnum; import com.alibaba.nacossync.extension.annotation.NacosSyncService; import com.alibaba.nacossync.pojo.model.TaskDO; -import java.util.concurrent.ConcurrentHashMap; +import com.alibaba.nacossync.util.StringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; @@ -27,6 +25,10 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Service; +import java.util.concurrent.ConcurrentHashMap; + +import static com.alibaba.nacossync.util.SkyWalkerUtil.generateSyncKey; + /** * @author NacosSync * @version $Id: SyncManagerService.java, v 0.1 2018-09-25 PM5:17 NacosSync Exp $$ @@ -60,11 +62,11 @@ public boolean sync(TaskDO taskDO, Integer index) { @Override public void afterPropertiesSet() { - this.applicationContext.getBeansOfType(SyncService.class).forEach((key, value) -> { + this.applicationContext.getBeansWithAnnotation(NacosSyncService.class).forEach((key, value) -> { NacosSyncService nacosSyncService = value.getClass().getAnnotation(NacosSyncService.class); ClusterTypeEnum sourceCluster = nacosSyncService.sourceCluster(); ClusterTypeEnum destinationCluster = nacosSyncService.destinationCluster(); - syncServiceMap.put(generateSyncKey(sourceCluster, destinationCluster), value); + syncServiceMap.put(generateSyncKey(sourceCluster, destinationCluster), (SyncService) value); }); } @@ -74,7 +76,9 @@ public void setApplicationContext(ApplicationContext applicationContext) throws } public SyncService getSyncService(String sourceClusterId, String destClusterId) { - + if (StringUtils.isEmpty(sourceClusterId) || StringUtils.isEmpty(destClusterId)) { + throw new IllegalArgumentException("Source cluster id and destination cluster id must not be null or empty"); + } ClusterTypeEnum sourceClusterType = this.skyWalkerCacheServices.getClusterType(sourceClusterId); ClusterTypeEnum destClusterType = this.skyWalkerCacheServices.getClusterType(destClusterId); diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/client/SyncQueryClient.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/client/SyncQueryClient.java index 1e88b1c6..d33368fb 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/client/SyncQueryClient.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/client/SyncQueryClient.java @@ -7,5 +7,6 @@ public interface SyncQueryClient { List getAllInstance(InstanceQueryModel instanceQueryModel); - + + } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/AbstractServerHolderImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/AbstractServerHolderImpl.java index dfde9e4d..6ff8e0c1 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/AbstractServerHolderImpl.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/holder/AbstractServerHolderImpl.java @@ -13,18 +13,19 @@ package com.alibaba.nacossync.extension.holder; import com.alibaba.nacossync.cache.SkyWalkerCacheServices; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; /** * @author paderlol * @date 2018-12-24 22:08 */ @Slf4j -public abstract class AbstractServerHolderImpl implements Holder { +public abstract class AbstractServerHolderImpl implements Holder { protected final Map serviceMap = new ConcurrentHashMap<>(); 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..e030d810 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,24 +13,19 @@ package com.alibaba.nacossync.extension.holder; import com.alibaba.nacos.api.PropertyKeyConst; -import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingService; -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 lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.springframework.stereotype.Service; + import java.util.List; import java.util.Optional; import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; 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; /** * @author paderlol @@ -42,29 +37,16 @@ public class NacosServerHolder extends AbstractServerHolderImpl { private final ClusterAccessService clusterAccessService; - private final TaskAccessService taskAccessService; - - private static ConcurrentHashMap globalNameService = new ConcurrentHashMap<>(16); - - public NacosServerHolder(ClusterAccessService clusterAccessService, TaskAccessService taskAccessService) { + public NacosServerHolder(ClusterAccessService clusterAccessService) { this.clusterAccessService = clusterAccessService; - this.taskAccessService = taskAccessService; } @Override NamingService createServer(String clusterId, Supplier serverAddressSupplier) throws Exception { - String newClusterId; - if (clusterId.contains(":")) { - String[] split = clusterId.split(":"); - newClusterId = split[1]; - } else { - newClusterId = clusterId; - } - //代表此时为组合key,确定target集群中的nameService是不同的 List allClusterConnectKey = skyWalkerCacheServices - .getAllClusterConnectKey(newClusterId); - ClusterDO clusterDO = clusterAccessService.findByClusterId(newClusterId); + .getAllClusterConnectKey(clusterId); + ClusterDO clusterDO = clusterAccessService.findByClusterId(clusterId); String serverList = Joiner.on(",").join(allClusterConnectKey); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList); @@ -77,51 +59,7 @@ 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; + return NamingFactory.createNamingService(properties); } - /** - * Get NamingService for different clients - * @param clusterId clusterId - * @return Returns Naming Service objects for different clusters - */ - public NamingService getNameService(String clusterId){ - return globalNameService.get(clusterId); - } - - public NamingService getSourceNamingService(String taskId, String sourceClusterId) { - String key = taskId + sourceClusterId; - return serviceMap.computeIfAbsent(key, k->{ - try { - log.info("Starting create source cluster server, key={}", key); - //代表此时为组合key,确定target集群中的nameService是不同的 - List allClusterConnectKey = skyWalkerCacheServices - .getAllClusterConnectKey(sourceClusterId); - ClusterDO clusterDO = clusterAccessService.findByClusterId(sourceClusterId); - TaskDO task = taskAccessService.findByTaskId(taskId); - 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) - ); - - Optional.ofNullable(clusterDO.getPassword()).ifPresent(value -> - properties.setProperty(PropertyKeyConst.PASSWORD, value) - ); - properties.setProperty(SkyWalkerConstants.SOURCE_CLUSTERID_KEY,task.getSourceClusterId()); - properties.setProperty(SkyWalkerConstants.DEST_CLUSTERID_KEY,task.getDestClusterId()); - return NamingFactory.createNamingService(properties); - }catch (NacosException e) { - log.error("start source server fail,taskId:{},sourceClusterId:{}" - , taskId, sourceClusterId, e); - return null; - } - }); - - } } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/AbstractNacosSync.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/AbstractNacosSync.java index 9e4bda19..e0e07f7a 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/AbstractNacosSync.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/AbstractNacosSync.java @@ -11,11 +11,16 @@ import com.alibaba.nacossync.extension.holder.NacosServerHolder; import com.alibaba.nacossync.monitor.MetricsManager; import com.alibaba.nacossync.pojo.model.TaskDO; +import com.alibaba.nacossync.util.BatchTaskExecutor; +import com.google.common.base.Stopwatch; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -38,63 +43,111 @@ public abstract class AbstractNacosSync implements SyncService { private final Map syncTaskTap = new ConcurrentHashMap<>(); private final ConcurrentHashMap allSyncTaskMap = new ConcurrentHashMap<>(); - + private ScheduledExecutorService executorService; @Autowired private MetricsManager metricsManager; + @Getter @Autowired private NacosServerHolder nacosServerHolder; /** - * 因为网络故障等原因,nacos sync的同步任务会失败,导致目标集群注册中心缺少同步实例, 为避免目标集群注册中心长时间缺少同步实例,每隔5分钟启动一个兜底工作线程执行一遍全部的同步任务。 + * Due to network issues or other reasons, the Nacos Sync synchronization tasks may fail, + * resulting in the target cluster's registry missing synchronized instances. + * To prevent the target cluster's registry from missing synchronized instances for an extended period, + * a fallback worker thread is started every 5 minutes to execute all synchronization tasks. */ @PostConstruct - public void startBasicSyncTaskThread() { - ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(r -> { + public void afterPropertiesSet() { + initializeExecutorService(); + scheduleSyncTasks(); + } + + @PreDestroy + public void destroy() { + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdown(); + try { + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException e) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + private void initializeExecutorService() { + executorService = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r); t.setDaemon(true); t.setName("com.alibaba.nacossync.basic.synctask"); return t; }); + } + + private void scheduleSyncTasks() { + executorService.scheduleWithFixedDelay(this::executeSyncTasks, 0, 300, TimeUnit.SECONDS); + } + + private void executeSyncTasks() { + if (allSyncTaskMap.isEmpty()) { + return; + } - executorService.scheduleWithFixedDelay(() -> { - if (allSyncTaskMap.size() == 0) { - return; - } - - try { - for (TaskDO taskDO : allSyncTaskMap.values()) { - String taskId = taskDO.getTaskId(); - NamingService sourceNamingService = nacosServerHolder.get(taskDO.getSourceClusterId()); - try { - doSync(taskId, taskDO, sourceNamingService); - } catch (Exception e) { - log.error("basic sync task process fail, taskId:{}", taskId, e); - metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); - } - } - } catch (Throwable e) { - log.warn("basic synctask thread error", e); - } - }, 0, 300, TimeUnit.SECONDS); + Collection taskCollections = allSyncTaskMap.values(); + List taskDOList = new ArrayList<>(taskCollections); + + if (CollectionUtils.isNotEmpty(taskDOList)) { + BatchTaskExecutor.batchOperation(taskDOList, this::executeTask); + } + } + + private void executeTask(TaskDO task) { + Stopwatch stopwatch = Stopwatch.createStarted(); + String taskId = task.getTaskId(); + try { + NamingService sourceNamingService = nacosServerHolder.get(task.getSourceClusterId()); + doSync(taskId, task, sourceNamingService); + } catch (NacosException e) { + log.error("sync task from nacos to nacos failed, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + } catch (Exception e) { + log.error("Unexpected error during sync task, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + } finally { + stopwatch.stop(); + log.debug("Task execution time for taskId {}: {} ms", taskId, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } } @Override public boolean delete(TaskDO taskDO) { + String taskId = taskDO.getTaskId(); try { + NamingService sourceNamingService = nacosServerHolder.get(taskDO.getSourceClusterId()); //移除订阅 + EventListener listener = listenerMap.remove(taskId); + if (listener!= null) { + sourceNamingService.unsubscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), listener); + } sourceNamingService.unsubscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), - listenerMap.remove(taskDO.getTaskId())); - sourceInstanceSnapshot.remove(taskDO.getTaskId()); - allSyncTaskMap.remove(taskDO.getTaskId()); + listenerMap.remove(taskId)); + sourceInstanceSnapshot.remove(taskId); + allSyncTaskMap.remove(taskId); // 删除目标集群中同步的实例列表 deregisterInstance(taskDO); + }catch (NacosException e) { + log.error("Delete task from nacos to specify destination was failed, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + return false; } catch (Exception e) { - log.error("delete task from nacos to specify destination was failed, taskId:{}", taskDO.getTaskId(), e); - metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR); + log.error("Unexpected error during sync task, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); return false; } return true; @@ -120,8 +173,12 @@ public boolean sync(TaskDO taskDO, Integer index) { }); sourceNamingService.subscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), listenerMap.get(taskId)); + }catch (NacosException e) { + log.error("Nacos sync task process fail, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + return false; } catch (Exception e) { - log.error("sync task from nacos to specify destination was failed, taskId:{}", taskId, e); + log.error("Unexpected error during sync task, taskId:{}", taskId, e); metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); return false; } @@ -205,7 +262,4 @@ public boolean needSync(Map sourceMetaData) { public abstract void removeInvalidInstance(TaskDO taskDO, Set invalidInstanceKeys) throws Exception; - public NacosServerHolder getNacosServerHolder() { - return nacosServerHolder; - } } 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 89eced92..a6d5f41a 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 @@ -18,9 +18,7 @@ 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.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacossync.cache.SkyWalkerCacheServices; import com.alibaba.nacossync.constant.ClusterTypeEnum; import com.alibaba.nacossync.constant.MetricsStatisticsType; @@ -31,19 +29,18 @@ import com.alibaba.nacossync.extension.holder.NacosServerHolder; import com.alibaba.nacossync.monitor.MetricsManager; 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.BatchTaskExecutor; import com.alibaba.nacossync.util.StringUtils; +import com.google.common.base.Stopwatch; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -60,125 +57,133 @@ @Slf4j @NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.NACOS) -public class NacosSyncToNacosServiceImpl implements SyncService, InitializingBean { +public class NacosSyncToNacosServiceImpl implements SyncService, InitializingBean, DisposableBean { - private Map listenerMap = new ConcurrentHashMap<>(); + private final Map listenerMap = new ConcurrentHashMap<>(); private final Map syncTaskTap = new ConcurrentHashMap<>(); - @Autowired - private MetricsManager metricsManager; + private final ConcurrentHashMap allSyncTaskMap = new ConcurrentHashMap<>(); - @Autowired - private SkyWalkerCacheServices skyWalkerCacheServices; + private ScheduledExecutorService executorService; - @Autowired - private NacosServerHolder nacosServerHolder; + private final MetricsManager metricsManager; - private ConcurrentHashMap allSyncTaskMap = new ConcurrentHashMap<>(); + private final NacosServerHolder nacosServerHolder; - @Autowired - private ClusterAccessService clusterAccessService; + private final ClusterAccessService clusterAccessService; - public static Map> serviceClient = new ConcurrentHashMap<>(); + private final SkyWalkerCacheServices skyWalkerCacheServices; - @Autowired - private FastSyncHelper fastSyncHelper; - - @Autowired - private TaskUpdateProcessor taskUpdateProcessor; + public NacosSyncToNacosServiceImpl(MetricsManager metricsManager, NacosServerHolder nacosServerHolder, + ClusterAccessService clusterAccessService, SkyWalkerCacheServices skyWalkerCacheServices) { + this.metricsManager = metricsManager; + this.nacosServerHolder = nacosServerHolder; + this.clusterAccessService = clusterAccessService; + this.skyWalkerCacheServices = skyWalkerCacheServices; + } /** - * 因为网络故障等原因,nacos sync的同步任务会失败,导致目标集群注册中心缺少同步实例, 为避免目标集群注册中心长时间缺少同步实例,每隔5分钟启动一个兜底工作线程执行一遍全部的同步任务。 + * Due to network issues or other reasons, the Nacos Sync synchronization tasks may fail, + * resulting in the target cluster's registry missing synchronized instances. + * To prevent the target cluster's registry from missing synchronized instances for an extended period, + * a fallback worker thread is started every 5 minutes to execute all synchronization tasks. */ - - @Override public void afterPropertiesSet() { - ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(r -> { + initializeExecutorService(); + scheduleSyncTasks(); + } + + @Override + public void destroy() { + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdown(); + try { + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException e) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + private void initializeExecutorService() { + executorService = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r); t.setDaemon(true); t.setName("com.alibaba.nacossync.basic.synctask"); return t; }); + } + + private void scheduleSyncTasks() { + executorService.scheduleWithFixedDelay(this::executeSyncTasks, 0, 300, TimeUnit.SECONDS); + } + + private void executeSyncTasks() { + if (allSyncTaskMap.isEmpty()) { + return; + } - executorService.scheduleWithFixedDelay(() -> { - if (allSyncTaskMap.size() == 0) { - return; - } - - try { - Collection taskCollections = allSyncTaskMap.values(); - List taskDOList = new ArrayList<>(taskCollections); - - if (CollectionUtils.isNotEmpty(taskDOList)) { - fastSyncHelper.syncWithThread(taskDOList, this::timeSync); - } - - } catch (Throwable e) { - log.warn("basic synctask thread error", e); - } - }, 0, 300, TimeUnit.SECONDS); + Collection taskCollections = allSyncTaskMap.values(); + List taskDOList = new ArrayList<>(taskCollections); + + if (CollectionUtils.isNotEmpty(taskDOList)) { + BatchTaskExecutor.batchOperation(taskDOList, this::executeTask); + } + } + + private void executeTask(TaskDO task) { + Stopwatch stopwatch = Stopwatch.createStarted(); + String taskId = task.getTaskId(); + try { + NamingService sourceNamingService = nacosServerHolder.get(task.getSourceClusterId()); + NamingService destNamingService = nacosServerHolder.get(task.getDestClusterId()); + doSync(taskId, task, sourceNamingService, destNamingService); + } catch (NacosException e) { + log.error("sync task from nacos to nacos failed, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + } catch (Exception e) { + log.error("Unexpected error during sync task, taskId:{}", taskId, e); + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + } finally { + stopwatch.stop(); + log.debug("Task execution time for taskId {}: {} ms", taskId, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } } @Override public boolean delete(TaskDO taskDO) { try { - NamingService sourceNamingService = nacosServerHolder.getSourceNamingService(taskDO.getTaskId(), - taskDO.getSourceClusterId()); + NamingService sourceNamingService = nacosServerHolder.get(taskDO.getSourceClusterId()); + int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId()); + String taskId = taskDO.getTaskId(); - if ("ALL".equals(taskDO.getServiceName())) { - String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskDO.getTaskId()); - if (!StringUtils.isEmpty(operationId)) { - allSyncTaskMap.remove(operationId); - } - - //处理group级别的服务任务删除 - ListView servicesOfServer = sourceNamingService.getServicesOfServer(0, Integer.MAX_VALUE, - taskDO.getGroupName()); - List serviceNames = servicesOfServer.getData(); - for (String serviceName : serviceNames) { - String operationKey = taskDO.getTaskId() + serviceName; - skyWalkerCacheServices.removeFinishedTask(operationKey); - allSyncTaskMap.remove(operationKey); - NamingService destNamingService = popNamingService(taskDO); - sourceNamingService.unsubscribe(serviceName, getGroupNameOrDefault(taskDO.getGroupName()), - listenerMap.remove(taskDO.getTaskId() + serviceName)); - - List sourceInstances = sourceNamingService.getAllInstances(serviceName, - getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), false); - for (Instance instance : sourceInstances) { - if (needSync(instance.getMetadata())) { - destNamingService.deregisterInstance(serviceName, - getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort()); - } - } - } - } else { - //处理服务级别的任务删除 - String operationId = taskUpdateProcessor.getTaskIdAndOperationIdMap(taskDO.getTaskId()); - 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); - - NamingService destNamingService = popNamingService(taskDO); - for (Instance instance : sourceInstances) { - if (needSync(instance.getMetadata())) { - destNamingService.deregisterInstance(taskDO.getServiceName(), - getGroupNameOrDefault(taskDO.getGroupName()), instance); - } + //Handle individual service + if (StringUtils.isEmpty(taskId)) { + log.warn("taskId is null data synchronization is not currently performed.{}", taskId); + return false; + } + EventListener listener = listenerMap.remove(taskId); + if (listener!= null) { + sourceNamingService.unsubscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), listener); + } + List sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(), + getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), false); + + NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId()); + for (Instance instance : sourceInstances) { + if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) { + destNamingService.deregisterInstance(taskDO.getServiceName(), + getGroupNameOrDefault(taskDO.getGroupName()), instance); } - // 移除任务 - skyWalkerCacheServices.removeFinishedTask(operationId); - // 移除所有需要同步的Task - allSyncTaskMap.remove(operationId); } + // Remove all tasks that need to be synchronized. + allSyncTaskMap.remove(taskId); + } catch (Exception e) { log.error("delete task from nacos to nacos was failed, operationalId:{}", taskDO.getOperationId(), e); metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR); @@ -189,75 +194,52 @@ public boolean delete(TaskDO taskDO) { @Override public boolean sync(TaskDO taskDO, Integer index) { - log.info("线程 {} 开始同步 {} ", Thread.currentThread().getId(), System.currentTimeMillis()); - String operationId = taskDO.getOperationId(); + log.info("Thread {} started synchronization at {}", Thread.currentThread().getId(), System.currentTimeMillis()); + String taskId = taskDO.getTaskId(); try { - NamingService sourceNamingService = nacosServerHolder.getSourceNamingService(taskDO.getTaskId(), - taskDO.getSourceClusterId()); - NamingService destNamingService = getDestNamingService(taskDO, index); - allSyncTaskMap.put(operationId, taskDO); - //防止暂停同步任务后,重新同步/或删除任务以后新建任务不会再接收到新的事件导致不能同步,所以每次订阅事件之前,先全量同步一次任务 - long startTime = System.currentTimeMillis(); - doSync(operationId, taskDO, sourceNamingService, destNamingService); - log.info("同步一个服务注册耗时:{} ms", System.currentTimeMillis() - startTime); - this.listenerMap.putIfAbsent(operationId, event -> { + NamingService sourceNamingService = nacosServerHolder.get(taskDO.getSourceClusterId()); + NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId()); + allSyncTaskMap.put(taskId, taskDO); + // To prevent issues where tasks paused for synchronization, newly created tasks after deletion, + // or resynchronization tasks do not receive new events and hence cannot synchronize, + // perform a full synchronization of tasks before subscribing to events each time. + Stopwatch stopwatch = Stopwatch.createStarted(); + doSync(taskId, taskDO, sourceNamingService, destNamingService); + log.debug("Time taken to synchronize a service registration: {} ms", + stopwatch.elapsed(TimeUnit.MILLISECONDS)); + this.listenerMap.putIfAbsent(taskId, event -> { if (event instanceof NamingEvent) { NamingEvent namingEvent = (NamingEvent) event; - log.info("监听到服务{}信息改变, taskId:{},实例数:{},发起同步", namingEvent.getServiceName(), - operationId, namingEvent.getInstances() == null ? null : namingEvent.getInstances().size()); + log.info("Detected changes in service {} information, taskId: {}, number of instances: {}, initiating synchronization", + namingEvent.getServiceName(), taskId, + namingEvent.getInstances() == null ? null : namingEvent.getInstances().size()); try { - doSync(operationId, taskDO, sourceNamingService, destNamingService); - log.info("监听到服务{}同步结束", namingEvent.getServiceName()); + doSync(taskId, taskDO, sourceNamingService, destNamingService); + log.info("Detected synchronization end for service {}", namingEvent.getServiceName()); } catch (Exception e) { - log.error("event process fail, operationId:{}", operationId, e); + log.error("event process fail, taskId:{}", taskId, e); metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); } } }); sourceNamingService.subscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), - listenerMap.get(operationId)); + listenerMap.get(taskId)); } catch (Exception e) { - log.error("sync task from nacos to nacos was failed, operationId:{}", operationId, e); + log.error("sync task from nacos to nacos was failed, taskId:{}", taskId, e); metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); return false; } return true; } - /** - * basic sync - * - * @param taskDO - */ - public void timeSync(TaskDO taskDO) { - log.debug("线程{}开始同步{}", Thread.currentThread().getId(), System.currentTimeMillis()); - String operationId = taskDO.getOperationId(); - try { - NamingService sourceNamingService = nacosServerHolder.getSourceNamingService(taskDO.getTaskId(), - taskDO.getSourceClusterId()); - //获取目标集群client - NamingService destNamingService = popNamingService(taskDO); - long startTime = System.currentTimeMillis(); - doSync(operationId, taskDO, sourceNamingService, destNamingService); - log.info("同步一个服务注册耗时:{} ms", System.currentTimeMillis() - startTime); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private NamingService getDestNamingService(TaskDO taskDO, Integer index) { - String key = taskDO.getSourceClusterId() + ":" + taskDO.getDestClusterId() + ":" + index; - return nacosServerHolder.get(key); - } private void doSync(String taskId, TaskDO taskDO, NamingService sourceNamingService, NamingService destNamingService) throws NacosException { if (syncTaskTap.putIfAbsent(taskId, 1) != null) { - log.info("任务Id:{}上一个同步任务尚未结束", taskId); + log.info("Task ID:{} - the previous synchronization task has not finished yet", taskId); return; } - //记录目标集群的Client - recordNamingService(taskDO, destNamingService); + try { List sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(), @@ -265,14 +247,15 @@ private void doSync(String taskId, TaskDO taskDO, NamingService sourceNamingServ int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId()); if (CollectionUtils.isNotEmpty(sourceInstances) && sourceInstances.get(0).isEphemeral()) { - //处临实例的批量数据同步,需要获取当前所有的服务实例,TODO,当Client为1.x的时候,执行和持久化实例一样的同步方式 + // Handle batch data synchronization of ephemeral instances, need to get all current service instances. + // TODO: When the Client is version 1.x, execute the same synchronization method as persistent instances. handlerPersistenceInstance(taskDO, destNamingService, sourceInstances, level); } else if (CollectionUtils.isEmpty(sourceInstances)) { - //如果当前源集群是空的 ,那么直接注销目标集群的实例 - log.debug("service {} need sync Ephemeral instance num is null: serviceName ", taskDO.getServiceName()); + // If the current source cluster is empty, then directly deregister the instances in the target cluster. + log.debug("service {} needs to sync ephemeral instance num is null: serviceName ", taskDO.getServiceName()); processDeRegisterInstances(taskDO, destNamingService); } else { - //处临持久化实例的批量数据同步 + // Handle batch data synchronization of persistent instances. handlerPersistenceInstance(taskDO, destNamingService, sourceInstances, level); } } finally { @@ -285,67 +268,78 @@ private void handlerPersistenceInstance(TaskDO taskDO, NamingService destNamingS List needRegisterInstance = new ArrayList<>(); for (Instance instance : sourceInstances) { if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) { - needRegisterInstance.add(instance); + needRegisterInstance.add(buildSyncInstance(instance, taskDO)); } } List destAllInstances = destNamingService.getAllInstances(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true); - // 获取目标集群自己已经同步的实例 + // Get the instances that the destination cluster has already synchronized List destHasSyncInstances = destAllInstances.stream() .filter(instance -> hasSync(instance, taskDO.getSourceClusterId())).collect(Collectors.toList()); - //获取新增的实例,遍历新增 - List newInstances = new ArrayList<>(needRegisterInstance); - instanceRemove(destHasSyncInstances, newInstances); - //注册 + // The following two conversions are necessary because the Nacos Instance's equals method + // is flawed and cannot be used for direct comparison. + // The reason is that Instance's equals method compares Metadata using the toString method, + // and Metadata is of type HashMap, which does not guarantee order in its toString representation. + + // Convert destHasSyncInstances to a Map with the concatenated string as key + Map destInstanceMap = destHasSyncInstances.stream() + .collect(Collectors.toMap(NacosSyncToNacosServiceImpl::getInstanceKey, instance -> instance)); + + // Convert newInstances to a Map with the concatenated string as key + Map needRegisterMap = needRegisterInstance.stream() + .collect(Collectors.toMap(NacosSyncToNacosServiceImpl::getInstanceKey, instance -> instance)); + + // Remove instances from newInstanceMap that are present in destInstanceMap + List newInstances = removeSyncedInstances(destInstanceMap, needRegisterMap); + + // Remove instances from destInstanceMap that are present in newInstanceMap + List invalidInstances = getInvalidInstances(destInstanceMap, needRegisterMap); + + // Register each instance one by one. Take one instance at a time. for (Instance newInstance : newInstances) { destNamingService.registerInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), - buildSyncInstance(newInstance, taskDO)); - } - - List notRemoveInstances = new ArrayList<>(); - for (Instance destHasSyncInstance : destHasSyncInstances) { - for (Instance instance : needRegisterInstance) { - if (instanceEquals(destHasSyncInstance, instance)) { - notRemoveInstances.add(destHasSyncInstance); - } - } + newInstance); } - destHasSyncInstances.removeAll(notRemoveInstances); - if (CollectionUtils.isNotEmpty(destHasSyncInstances)) { - log.info("taskid:{},服务 {} 发生反注册,执行数量 {} ", taskDO.getTaskId(), taskDO.getServiceName(), + if (CollectionUtils.isNotEmpty(invalidInstances)) { + log.info("taskId: {}, service {} deregistered, number of executions: {}", taskDO.getTaskId(), taskDO.getServiceName(), destHasSyncInstances.size()); } - - for (Instance destAllInstance : destHasSyncInstances) { + for (Instance instance : invalidInstances) { destNamingService.deregisterInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), - destAllInstance); + instance); } } - - 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() - == ins2.isEphemeral()) && (ins1.getClusterName().equals(ins2.getClusterName())) - && (ins1.getServiceName().equals(ins2.getServiceName())); + private List getInvalidInstances(Map destInstanceMap, Map needRegisterMap) { + Map destClone = new HashMap<>(destInstanceMap); + + // Convert newInstances to a Map with the concatenated string as key + Map needRegisterClone = new HashMap<>(needRegisterMap); + + // Remove instances from newInstanceMap that are present in destInstanceMap + destClone.keySet().removeAll(needRegisterClone.keySet()); + + return new ArrayList<>(destClone.values()); } - private void instanceRemove(List destHasSyncInstances, List newInstances) { - List needRemoveInstance = new ArrayList<>(); - for (Instance destHasSyncInstance : destHasSyncInstances) { - for (Instance newInstance : newInstances) { - if (destHasSyncInstance.equals(newInstance)) { - //如果目标集群已经存在了源集群同步过来的实例,就不需要同步了 - needRemoveInstance.add(newInstance); - } - } - } - // eg:A Cluster 已经同步到 B Cluster的实例数据,就不需要再重复同步过来了 - newInstances.removeAll(needRemoveInstance); + + public List removeSyncedInstances(Map destInstanceMap, Map needRegisterMap) { + // Convert destHasSyncInstances to a Map with the concatenated string as key + + Map destClone = new HashMap<>(destInstanceMap); + + // Convert newInstances to a Map with the concatenated string as key + Map needRegisterClone = new HashMap<>(needRegisterMap); + + // Remove instances from newInstanceMap that are present in destInstanceMap + needRegisterClone.keySet().removeAll(destClone.keySet()); + + return new ArrayList<>(needRegisterClone.values()); } + private boolean hasSync(Instance instance, String sourceClusterId) { if (instance.getMetadata() != null) { @@ -357,23 +351,25 @@ private boolean hasSync(Instance instance, String sourceClusterId) { /** - * 当源集群需要同步的实例个数为0时,目标集群如果还有源集群同步的实例,执行反注册 + * When the number of instances that the source cluster needs to synchronize is 0, + * if the target cluster still has instances synchronized with the source cluster, + * perform unregistration. * - * @param taskDO * @param destNamingService * @throws NacosException */ private void processDeRegisterInstances(TaskDO taskDO, NamingService destNamingService) throws NacosException { - //如果此时sourceInstance中的实例为空,证明此时实例下线或实例不存在 + // If the instances in sourceInstances are empty, it means the instances are offline or do not exist. List destInstances = destNamingService.getAllInstances(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), false); - // 如果目标集群中的数据实例也为空了,则测试无需操作 + // If the instances in the target cluster are also empty, then no operation is needed. if (CollectionUtils.isEmpty(destInstances)) { return; } - deRegisterFilter(destInstances, taskDO.getSourceClusterId()); + destInstances = filterInstancesForRemoval(destInstances, taskDO.getSourceClusterId()); if (CollectionUtils.isNotEmpty(destInstances)) { - //逐个执行反注册,拿出一个实例即可, 需要处理redo,否则会被重新注册上来 + // Deregister each instance one by one. Take one instance at a time. + // Need to handle redo, otherwise, it will be registered again. for (Instance destInstance : destInstances) { destNamingService.deregisterInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()), destInstance); @@ -381,17 +377,11 @@ private void processDeRegisterInstances(TaskDO taskDO, NamingService destNamingS } } - private void deRegisterFilter(List destInstances, String sourceClusterId) { - List newDestInstance = 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); - } - } - destInstances = newDestInstance; + private List filterInstancesForRemoval(List destInstances, String sourceClusterId) { + return destInstances.stream().filter(instance -> !instance.getMetadata().isEmpty()) + .filter(instance -> needDeregister(instance.getMetadata().get(SOURCE_CLUSTERID_KEY), sourceClusterId)) + .collect(Collectors.toList()); + } private boolean needDeregister(String destClusterId, String sourceClusterId) { @@ -402,42 +392,31 @@ private boolean needDeregister(String destClusterId, String sourceClusterId) { } private boolean needSync(Map sourceMetaData, int level, String destClusterId) { - //普通集群(默认) + // Regular cluster (default) if (level == 0) { return SyncService.super.needSync(sourceMetaData); } - //中心集群,只要不是目标集群传过来的实例,都需要同步(扩展功能) - if (!destClusterId.equals(sourceMetaData.get(SOURCE_CLUSTERID_KEY))) { - return true; - } - return false; + // Central cluster, as long as the instance is not from the target cluster, + // it needs to be synchronized (extended functionality) + return !destClusterId.equals(sourceMetaData.get(SOURCE_CLUSTERID_KEY)); } - private void recordNamingService(TaskDO taskDO, NamingService destNamingService) { - String key = buildClientKey(taskDO); - serviceClient.computeIfAbsent(key, clientKey -> { - Set hashSet = new ConcurrentHashSet<>(); - hashSet.add(destNamingService); - return hashSet; - }); - } - - public NamingService popNamingService(TaskDO taskDO) { - String key = buildClientKey(taskDO); - Set namingServices = serviceClient.get(key); - if (CollectionUtils.isNotEmpty(namingServices)) { - return namingServices.iterator().next(); - } - log.warn("{} 无可用 namingservice", key); - return null; - } - private static String buildClientKey(TaskDO taskDO) { - return taskDO.getId() + ":" + taskDO.getServiceName(); + private Instance buildSyncInstance(Instance instance, TaskDO taskDO) { + Instance temp = getInstance(instance); + temp.addMetadata(SkyWalkerConstants.DEST_CLUSTERID_KEY, taskDO.getDestClusterId()); + temp.addMetadata(SkyWalkerConstants.SYNC_SOURCE_KEY, + skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode()); + temp.addMetadata(SkyWalkerConstants.SOURCE_CLUSTERID_KEY, taskDO.getSourceClusterId()); + //The flag is a synchronous instance + temp.addMetadata(SkyWalkerConstants.SYNC_INSTANCE_TAG, + taskDO.getSourceClusterId() + "@@" + taskDO.getVersion()); + return temp; } - private Instance buildSyncInstance(Instance instance, TaskDO taskDO) { + private static Instance getInstance(Instance instance) { Instance temp = new Instance(); + temp.setInstanceId(instance.getInstanceId()); temp.setIp(instance.getIp()); temp.setPort(instance.getPort()); temp.setClusterName(instance.getClusterName()); @@ -447,15 +426,18 @@ private Instance buildSyncInstance(Instance instance, TaskDO taskDO) { 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.SYNC_INSTANCE_TAG, taskDO.getSourceClusterId() + "@@" + taskDO.getVersion()); temp.setMetadata(metaData); return temp; } - + private static String getInstanceKey(Instance instance) { + return String.join("|", + instance.getIp(), + String.valueOf(instance.getPort()), + String.valueOf(instance.getWeight()), + String.valueOf(instance.isHealthy()), + String.valueOf(instance.isEphemeral()), + instance.getClusterName(), + instance.getServiceName()); + } } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToZookeeperServiceImpl.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToZookeeperServiceImpl.java index b746d2d6..da81d05c 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToZookeeperServiceImpl.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/extension/impl/NacosSyncToZookeeperServiceImpl.java @@ -12,10 +12,6 @@ */ package com.alibaba.nacossync.extension.impl; -import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault; -import static com.alibaba.nacossync.util.StringUtils.convertDubboFullPathForZk; -import static com.alibaba.nacossync.util.StringUtils.convertDubboProvidersPath; - import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; @@ -33,6 +29,14 @@ import com.alibaba.nacossync.pojo.model.TaskDO; import com.alibaba.nacossync.util.DubboConstants; import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.cache.PathChildrenCache; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.curator.utils.CloseableUtils; +import org.apache.zookeeper.CreateMode; +import org.springframework.beans.factory.annotation.Autowired; + import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; @@ -41,13 +45,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import lombok.extern.slf4j.Slf4j; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.recipes.cache.PathChildrenCache; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.apache.curator.utils.CloseableUtils; -import org.apache.zookeeper.CreateMode; -import org.springframework.beans.factory.annotation.Autowired; + +import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault; +import static com.alibaba.nacossync.util.StringUtils.convertDubboFullPathForZk; +import static com.alibaba.nacossync.util.StringUtils.convertDubboProvidersPath; /** * Nacos 同步 Zk 数据 @@ -113,8 +114,10 @@ public boolean delete(TaskDO taskDO) { CloseableUtils.closeQuietly(pathChildrenCache); Set instanceUrlSet = instanceBackupMap.get(taskDO.getTaskId()); CuratorFramework client = zookeeperServerHolder.get(taskDO.getDestClusterId()); - for (String instanceUrl : instanceUrlSet) { - client.delete().quietly().forPath(instanceUrl); + if(!instanceUrlSet.isEmpty()){ + for (String instanceUrl : instanceUrlSet) { + client.delete().quietly().forPath(instanceUrl); + } } } catch (Exception e) { log.error("delete task from nacos to zk was failed, taskId:{}", taskDO.getTaskId(), e); @@ -161,28 +164,45 @@ public boolean sync(TaskDO taskDO, Integer index) { } return true; } - + private void tryToCompensate(TaskDO taskDO, NamingService sourceNamingService, List sourceInstances) { if (!CollectionUtils.isEmpty(sourceInstances)) { final PathChildrenCache pathCache = getPathCache(taskDO); - if (pathCache.getListenable().size() == 0) { // 防止重复注册 - pathCache.getListenable().addListener((zkClient, zkEvent) -> { - if (zkEvent.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) { - List allInstances = - sourceNamingService.getAllInstances(taskDO.getServiceName(),getGroupNameOrDefault(taskDO.getGroupName())); - for (Instance instance : allInstances) { - String instanceUrl = buildSyncInstance(instance, taskDO); - String zkInstancePath = zkEvent.getData().getPath(); - if (zkInstancePath.equals(instanceUrl)) { - zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL) - .forPath(zkInstancePath); - break; - } - } - } - }); + // Avoiding re-registration if there is already a listener registered. + if (pathCache.getListenable().size() == 0) { + registerCompensationListener(pathCache, taskDO, sourceNamingService); } - + } + } + + private void registerCompensationListener(PathChildrenCache pathCache, TaskDO taskDO, NamingService sourceNamingService) { + pathCache.getListenable().addListener((zkClient, zkEvent) -> { + try { + if (zkEvent.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) { + compensateOnChildRemoval(zkClient, zkEvent, sourceNamingService, taskDO); + } + } catch (Exception e) { + log.error("Error processing ZooKeeper event: {}", zkEvent.getType(), e); + } + }); + } + + private void compensateOnChildRemoval(CuratorFramework zkClient, PathChildrenCacheEvent zkEvent, NamingService sourceNamingService, TaskDO taskDO) { + String zkInstancePath = null; + try { + List allInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName())); + zkInstancePath = zkEvent.getData().getPath(); + for (Instance instance : allInstances) { + String instanceUrl = buildSyncInstance(instance, taskDO); + if (zkInstancePath.equals(instanceUrl)) { + zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL) + .forPath(zkInstancePath); + log.info("Compensated by re-creating the removed node at path: {}", zkInstancePath); + break; + } + } + } catch (Exception e) { + log.error("Failed to compensate for the removed node at path: {}", zkInstancePath, e); } } @@ -215,8 +235,7 @@ private HashSet getWaitingToAddInstance(TaskDO taskDO, CuratorFramework } protected String buildSyncInstance(Instance instance, TaskDO taskDO) throws UnsupportedEncodingException { - Map metaData = new HashMap<>(); - metaData.putAll(instance.getMetadata()); + 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()); 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..27aef3bc 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 @@ -35,13 +35,14 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; +import java.util.Collections; 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.function.Predicate; import static com.alibaba.nacossync.util.DubboConstants.ALL_SERVICE_NAME_PATTERN; import static com.alibaba.nacossync.util.DubboConstants.DUBBO_PATH_FORMAT; @@ -68,6 +69,8 @@ @NacosSyncService(sourceCluster = ClusterTypeEnum.ZK, destinationCluster = ClusterTypeEnum.NACOS) public class ZookeeperSyncToNacosServiceImpl implements SyncService { + private static final String DEFAULT_WEIGHT = "1.0"; + @Autowired private MetricsManager metricsManager; @@ -101,26 +104,18 @@ public boolean sync(TaskDO taskDO, Integer index) { if (treeCacheMap.containsKey(taskDO.getTaskId())) { return true; } + if (!initializeTreeCache(taskDO)) { + return false; + } - TreeCache treeCache = getTreeCache(taskDO); NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId()); + if (destNamingService == null) { + logAndRecordSyncError("Failed to obtain NamingService for destination clusterId: {}", taskDO.getDestClusterId(), null); + return false; + } // 初次执行任务统一注册所有实例 registerAllInstances(taskDO, destNamingService); - //注册ZK监听 - Objects.requireNonNull(treeCache).getListenable().addListener((client, event) -> { - try { - - String path = event.getData().getPath(); - Map queryParam = parseQueryString(path); - if (isMatch(taskDO, queryParam) && needSync(queryParam)) { - processEvent(taskDO, destNamingService, event, path, queryParam); - } - } catch (Exception e) { - log.error("event process from Zookeeper to Nacos was failed, taskId:{}", taskDO.getTaskId(), e); - metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); - } - - }); + setupListener(taskDO, destNamingService); } catch (Exception e) { log.error("sync task from Zookeeper to Nacos was failed, taskId:{}", taskDO.getTaskId(), e); metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); @@ -128,6 +123,180 @@ public boolean sync(TaskDO taskDO, Integer index) { } return true; } + private boolean initializeTreeCache(TaskDO taskDO) { + TreeCache treeCache = getTreeCache(taskDO); + if (treeCache == null) { + logAndRecordSyncError("Failed to obtain TreeCache for taskId: {}", taskDO.getTaskId(), null); + return false; + } + return true; + } + private void logAndRecordSyncError(String message, String taskId, Exception e) { + if (e != null) { + log.error(message, taskId, e); + } else { + log.error(message, taskId); + } + metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR); + } + + private void setupListener(TaskDO taskDO, NamingService destNamingService) { + TreeCache treeCache = Objects.requireNonNull(getTreeCache(taskDO)); + treeCache.getListenable().addListener((client, event) -> { + try { + // INITIALIZED is a special event that is not triggered by the Zookeeper server + if(event.getData()==null){ + log.warn("TreeCache event data is null, taskId:{}", taskDO.getTaskId()); + return; + } + String path = event.getData().getPath(); + Map queryParam = parseQueryString(path); + if (isMatch(taskDO, queryParam) && needSync(queryParam)) { + processEvent(taskDO, destNamingService, event, path, queryParam); + } + } catch (Exception e) { + logAndRecordSyncError("Event process from Zookeeper to Nacos was failed, taskId:{}", taskDO.getTaskId(), e); + } + }); + } + + + + public boolean delete(TaskDO taskDO) { + if (taskDO.getServiceName() == null) { + return true; + } + + CloseableUtils.closeQuietly(treeCacheMap.get(taskDO.getTaskId())); + + try { + NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId()); + if (destNamingService == null) { + log.error("Failed to obtain NamingService for destination clusterId: {}", taskDO.getDestClusterId()); + return false; + } + + Set serviceNames = getServiceNamesToDelete(taskDO); + deleteInstances(serviceNames, destNamingService, taskDO); + + } catch (Exception e) { + log.error("Delete task from Zookeeper to Nacos was failed, taskId:{}", taskDO.getTaskId(), e); + metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR); + return false; + } + + return true; + } + + private Set getServiceNamesToDelete(TaskDO taskDO) { + if (!ALL_SERVICE_NAME_PATTERN.equals(taskDO.getServiceName())) { + return new HashSet<>(Collections.singleton(taskDO.getServiceName())); + } else { + return new HashSet<>(nacosServiceNameMap.keySet()); + } + } + + private void deleteInstances(Set serviceNames, NamingService destNamingService, TaskDO taskDO) { + for (String serviceName : serviceNames) { + try { + 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); + } catch (NacosException e) { + log.error("Failed to deregister service instance for serviceName: {}", serviceName, e); + } + } + } + + /** + * fetch the Path cache when the task sync + */ + protected TreeCache getTreeCache(TaskDO taskDO) { + return treeCacheMap.computeIfAbsent(taskDO.getTaskId(), (key) -> { + try { + TreeCache treeCache = new TreeCache(zookeeperServerHolder.get(taskDO.getSourceClusterId()), + DUBBO_ROOT_PATH); + treeCache.start(); + return treeCache; + } catch (Exception e) { + log.error("zookeeper path children cache start failed, taskId:{}", taskDO.getTaskId(), e); + return null; + } + }); + + } + + /** + * Checks if the instance information needs to be synchronized based on the dubbo version, grouping name, + * and service name. + */ + protected boolean isMatch(TaskDO taskDO, Map queryParam) { + return isVersionMatch(taskDO, queryParam) && + isGroupMatch(taskDO, queryParam) && + isServiceMatch(taskDO, queryParam) || + isMatchAllServices(taskDO); + } + + private boolean isVersionMatch(TaskDO task, Map queryParam) { + return StringUtils.isBlank(task.getVersion()) || StringUtils.equals(task.getVersion(), queryParam.get(VERSION_KEY)); + } + + private boolean isGroupMatch(TaskDO task, Map queryParam) { + return StringUtils.isBlank(task.getGroupName()) || StringUtils.equals(task.getGroupName(), queryParam.get(GROUP_KEY)); + } + + private boolean isServiceMatch(TaskDO task, Map queryParam) { + return StringUtils.isNotBlank(task.getServiceName()) && StringUtils.equals(task.getServiceName(), queryParam.get(INTERFACE_KEY)); + } + + private boolean isMatchAllServices(TaskDO task) { + return StringUtils.isNotBlank(task.getServiceName()) && StringUtils.equals(task.getServiceName(), ALL_SERVICE_NAME_PATTERN); + } + + + /** + * Builds a synchronized Nacos instance from Zookeeper data. + * + * @param queryParam Parameters obtained from the query string. + * @param ipAndPortMap IP and port information. + * @param taskDO Task details. + * @return A fully configured Nacos instance. + */ + protected Instance buildSyncInstance(Map queryParam, Map ipAndPortMap, TaskDO taskDO) { + Instance instance = new Instance(); + instance.setIp(ipAndPortMap.get(INSTANCE_IP_KEY)); + instance.setPort(Integer.parseInt(ipAndPortMap.get(INSTANCE_PORT_KEY))); + instance.setServiceName(getServiceNameFromCache(taskDO.getTaskId(), queryParam)); + instance.setWeight(parseWeight(queryParam)); + instance.setHealthy(true); + instance.setMetadata(buildMetadata(queryParam, ipAndPortMap, taskDO)); + return instance; + } + + + private double parseWeight(Map queryParam) { + try { + return Double.parseDouble(queryParam.getOrDefault(WEIGHT_KEY, DEFAULT_WEIGHT)); + } catch (NumberFormatException e) { + log.error("Error parsing weight: {}", queryParam.get(WEIGHT_KEY), e); + return Double.parseDouble(DEFAULT_WEIGHT); // Default weight in case of error + } + } + + private Map buildMetadata(Map queryParam, Map ipAndPortMap, TaskDO taskDO) { + Map metaData = new HashMap<>(queryParam); + metaData.put(PROTOCOL_KEY, ipAndPortMap.get(PROTOCOL_KEY)); + 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()); + return metaData; + } private void processEvent(TaskDO taskDO, NamingService destNamingService, TreeCacheEvent event, String path, Map queryParam) throws NacosException { @@ -136,8 +305,16 @@ private void processEvent(TaskDO taskDO, NamingService destNamingService, TreeCa } Map ipAndPortParam = parseIpAndPortString(path); + if (ipAndPortParam.isEmpty()) { + log.error("Invalid IP and Port data extracted from path: {}", path); + return; + } Instance instance = buildSyncInstance(queryParam, ipAndPortParam, taskDO); String serviceName = queryParam.get(INTERFACE_KEY); + if (serviceName == null || serviceName.isEmpty()) { + log.error("Service name is missing in the query parameters."); + return; + } switch (event.getType()) { case NODE_ADDED: case NODE_UPDATED: @@ -188,116 +365,6 @@ private void registerALLInstances0(TaskDO taskDO, NamingService destNamingServic } } - @Override - public boolean delete(TaskDO taskDO) { - if (taskDO.getServiceName() == null) { - return true; - } - try { - - 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()); - - } - } - } 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); - - } - } - } - } - - - } catch (Exception e) { - log.error("delete task from zookeeper to nacos was failed, taskId:{}", taskDO.getTaskId(), e); - metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR); - return false; - } - return true; - } - - /** - * fetch the Path cache when the task sync - */ - protected TreeCache getTreeCache(TaskDO taskDO) { - return treeCacheMap.computeIfAbsent(taskDO.getTaskId(), (key) -> { - try { - TreeCache treeCache = new TreeCache(zookeeperServerHolder.get(taskDO.getSourceClusterId()), - DUBBO_ROOT_PATH); - treeCache.start(); - return treeCache; - } catch (Exception e) { - log.error("zookeeper path children cache start failed, taskId:{}", taskDO.getTaskId(), e); - return null; - } - }); - - } - - /** - * The instance information that needs to be synchronized is matched based on the dubbo version and the grouping - * name - */ - protected boolean isMatch(TaskDO taskDO, Map queryParam) { - Predicate isVersionEq = (task) -> StringUtils.isBlank(taskDO.getVersion()) || StringUtils.equals( - task.getVersion(), queryParam.get(VERSION_KEY)); - Predicate isGroupEq = (task) -> StringUtils.isBlank(taskDO.getGroupName()) || StringUtils.equals( - task.getGroupName(), queryParam.get(GROUP_KEY)); - Predicate isServiceEq = (task) -> StringUtils.isNotBlank(taskDO.getServiceName()) && StringUtils.equals( - task.getServiceName(), queryParam.get(INTERFACE_KEY)); - Predicate isMatchAll = (task) -> StringUtils.isNotBlank(taskDO.getServiceName()) && StringUtils.equals( - taskDO.getServiceName(), ALL_SERVICE_NAME_PATTERN); - return isVersionEq.and(isGroupEq).and(isServiceEq).or(isMatchAll).test(taskDO); - } - - /** - * create Nacos service instance - * - * @param queryParam dubbo metadata - * @param ipAndPortMap dubbo ip and address - */ - protected Instance buildSyncInstance(Map queryParam, Map ipAndPortMap, - TaskDO taskDO) { - Instance temp = new Instance(); - temp.setIp(ipAndPortMap.get(INSTANCE_IP_KEY)); - temp.setPort(Integer.parseInt(ipAndPortMap.get(INSTANCE_PORT_KEY))); - temp.setServiceName(getServiceNameFromCache(taskDO.getTaskId(), queryParam)); - temp.setWeight(Double.parseDouble(queryParam.get(WEIGHT_KEY) == null ? "1.0" : queryParam.get(WEIGHT_KEY))); - temp.setHealthy(true); - - Map metaData = new HashMap<>(queryParam); - metaData.put(PROTOCOL_KEY, ipAndPortMap.get(PROTOCOL_KEY)); - 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()); - temp.setMetadata(metaData); - return temp; - } /** * cteate Dubbo service name diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/pojo/model/TaskDO.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/pojo/model/TaskDO.java index 7b6f0a8f..1b65993d 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/pojo/model/TaskDO.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/pojo/model/TaskDO.java @@ -71,8 +71,4 @@ public class TaskDO implements Serializable { */ private String operationId; - /** - * current task status - */ - private Integer status; } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/ClusterAddProcessor.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/ClusterAddProcessor.java index 970b12fe..5b99725e 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/ClusterAddProcessor.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/ClusterAddProcessor.java @@ -53,7 +53,7 @@ public void process(ClusterAddRequest clusterAddRequest, ClusterAddResult cluste Object... others) throws Exception { ClusterDO clusterDO = new ClusterDO(); - if (null == clusterAddRequest.getConnectKeyList() || 0 == clusterAddRequest.getConnectKeyList().size()) { + if (null == clusterAddRequest.getConnectKeyList() || clusterAddRequest.getConnectKeyList().isEmpty()) { throw new SkyWalkerException("集群列表不能为空!"); } @@ -83,6 +83,7 @@ public void process(ClusterAddRequest clusterAddRequest, ClusterAddResult cluste clusterDO.setUserName(clusterAddRequest.getUserName()); clusterDO.setPassword(clusterAddRequest.getPassword()); clusterDO.setNamespace(clusterAddRequest.getNamespace()); + clusterDO.setClusterLevel(0); clusterAccessService.insert(clusterDO); } } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskDeleteProcessor.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskDeleteProcessor.java index 2f18186e..9cfd8e4e 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskDeleteProcessor.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskDeleteProcessor.java @@ -14,9 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.alibaba.nacossync.template.processor; +import com.alibaba.nacossync.constant.SkyWalkerConstants; import com.alibaba.nacossync.dao.TaskAccessService; +import com.alibaba.nacossync.event.DeleteAllSubTaskEvent; import com.alibaba.nacossync.event.DeleteTaskEvent; import com.alibaba.nacossync.pojo.model.TaskDO; import com.alibaba.nacossync.pojo.request.TaskDeleteRequest; @@ -24,7 +27,6 @@ import com.alibaba.nacossync.template.Processor; import com.google.common.eventbus.EventBus; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** @@ -34,18 +36,28 @@ @Slf4j @Service public class TaskDeleteProcessor implements Processor { - - @Autowired - private TaskAccessService taskAccessService; - @Autowired - private EventBus eventBus; - + + private final TaskAccessService taskAccessService; + + private final EventBus eventBus; + + public TaskDeleteProcessor(TaskAccessService taskAccessService, EventBus eventBus) { + this.taskAccessService = taskAccessService; + this.eventBus = eventBus; + } + @Override - public void process(TaskDeleteRequest taskDeleteRequest, BaseResult baseResult, - Object... others) { + public void process(TaskDeleteRequest taskDeleteRequest, BaseResult baseResult, Object... others) { TaskDO taskDO = taskAccessService.findByTaskId(taskDeleteRequest.getTaskId()); - eventBus.post(new DeleteTaskEvent(taskDO)); - log.info("删除同步任务数据之前,发出一个同步事件:" + taskDO); + // delete all sub task when ServiceName is all + if (SkyWalkerConstants.NACOS_ALL_SERVICE_NAME.equalsIgnoreCase(taskDO.getServiceName())) { + eventBus.post(new DeleteAllSubTaskEvent(taskDO)); + } else { + eventBus.post(new DeleteTaskEvent(taskDO)); + } + log.info("删除同步任务数据之前,发出一个同步事件:{}", taskDO); taskAccessService.deleteTaskById(taskDeleteRequest.getTaskId()); } + + } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskUpdateProcessor.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskUpdateProcessor.java index 8b8d4e4a..327c7dc2 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskUpdateProcessor.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/template/processor/TaskUpdateProcessor.java @@ -16,22 +16,17 @@ */ package com.alibaba.nacossync.template.processor; -import com.alibaba.nacossync.util.SkyWalkerUtil; -import lombok.extern.slf4j.Slf4j; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import com.alibaba.nacossync.constant.TaskStatusEnum; import com.alibaba.nacossync.dao.TaskAccessService; import com.alibaba.nacossync.exception.SkyWalkerException; -import com.alibaba.nacossync.pojo.result.BaseResult; import com.alibaba.nacossync.pojo.model.TaskDO; import com.alibaba.nacossync.pojo.request.TaskUpdateRequest; +import com.alibaba.nacossync.pojo.result.BaseResult; import com.alibaba.nacossync.template.Processor; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import com.alibaba.nacossync.util.SkyWalkerUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /** * @author NacosSync @@ -43,8 +38,6 @@ public class TaskUpdateProcessor implements Processor taskIdAndOperationIdMap = new ConcurrentHashMap<>(); - @Override public void process(TaskUpdateRequest taskUpdateRequest, BaseResult baseResult, Object... others) throws Exception { @@ -63,15 +56,12 @@ public void process(TaskUpdateRequest taskUpdateRequest, BaseResult baseResult, } taskDO.setTaskStatus(taskUpdateRequest.getTaskStatus()); - //在id生成之前保存好操作id,可以在删除操作里面进行 - taskIdAndOperationIdMap.put(taskDO.getTaskId(),taskDO.getOperationId()); + taskDO.setOperationId(SkyWalkerUtil.generateOperationId()); taskAccessService.addTask(taskDO); } - public String getTaskIdAndOperationIdMap(String taskId) { - return taskIdAndOperationIdMap.remove(taskId); - } + } diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllNacosThread.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllNacosThread.java new file mode 100644 index 00000000..c126e7bf --- /dev/null +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllNacosThread.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacossync.timer; + +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.client.naming.utils.CollectionUtils; +import com.alibaba.nacossync.constant.MetricsStatisticsType; +import com.alibaba.nacossync.constant.TaskStatusEnum; +import com.alibaba.nacossync.dao.TaskAccessService; +import com.alibaba.nacossync.event.DeleteAllSubTaskEvent; +import com.alibaba.nacossync.event.DeleteTaskEvent; +import com.alibaba.nacossync.event.SyncTaskEvent; +import com.alibaba.nacossync.extension.holder.NacosServerHolder; +import com.alibaba.nacossync.monitor.MetricsManager; +import com.alibaba.nacossync.pojo.model.TaskDO; +import com.alibaba.nacossync.util.BatchTaskExecutor; +import com.alibaba.nacossync.util.SkyWalkerUtil; +import com.google.common.eventbus.EventBus; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * when the database task service name is empty, check all the services in the cluster and create a synchronization + * task. + */ +@Slf4j +public class CheckRunningStatusAllNacosThread implements Runnable { + + private final Map> subTaskService = new ConcurrentHashMap<>(); + + private final MetricsManager metricsManager; + + private final TaskAccessService taskAccessService; + + private final NacosServerHolder nacosServerHolder; + + private final EventBus eventBus; + + + public CheckRunningStatusAllNacosThread(MetricsManager metricsManager, TaskAccessService taskAccessService, + NacosServerHolder nacosServerHolder, EventBus eventBus) { + this.metricsManager = metricsManager; + this.taskAccessService = taskAccessService; + this.nacosServerHolder = nacosServerHolder; + this.eventBus = eventBus; + } + + /** + * Synchronize data based on the ns level. + */ + @Override + public void run() { + + try { + List tasks = taskAccessService.findAllByServiceNameEqualAll(); + if (CollectionUtils.isEmpty(tasks)) { + return; + } + + // Get the set of all task IDs + Set taskIdSet = tasks.stream().map(TaskDO::getTaskId).collect(Collectors.toSet()); + + // Filter and handle sub-tasks that need to be deleted. This handles the case where tasks have been deleted + // but sub-tasks still exist. + subTaskService.entrySet().stream() + .filter(entry -> shouldDeleteSubTasks(entry, taskIdSet)) + .forEach(entry -> postDeleteAllSubTaskEvent(entry.getKey())); + + // Handle regular tasks + tasks.forEach(this::processTask); + } catch (Exception e) { + log.warn("CheckRunningStatusThread Exception", e); + metricsManager.recordError(MetricsStatisticsType.DISPATCHER_TASK); + } + } + + /** + * Listens for the event of deleting all sub-tasks and handles the delete operation. + * + * @param deleteAllSubTaskEvent The event object containing the task information to be deleted. + */ + public void listenerDeleteAllTaskEvent(DeleteAllSubTaskEvent deleteAllSubTaskEvent) { + // Retrieve the task object + TaskDO task = deleteAllSubTaskEvent.getTaskDO(); + + // Retrieve the task ID + String taskId = task.getTaskId(); + + // Remove the set of service names corresponding to the task ID from subTaskService + Set serviceNameSet = subTaskService.remove(taskId); + + // If the set of service names is empty, return immediately + if (CollectionUtils.isEmpty(serviceNameSet)) { + return; + } + + // Build the list of sub-tasks pending removal + List servicesPendingRemoval = serviceNameSet.stream() + .map(serviceName -> buildSubTaskDO(task, serviceName)) + .collect(Collectors.toUnmodifiableList()); + + // Handle the removal of the pending sub-tasks + handleRemoval(servicesPendingRemoval, serviceNameSet); + } + + + private boolean shouldDeleteSubTasks(Map.Entry> entry, Set taskIdSet) { + return !taskIdSet.contains(entry.getKey()) && !entry.getValue().isEmpty(); + } + + private void postDeleteAllSubTaskEvent(String taskId) { + TaskDO taskDO = new TaskDO(); + taskDO.setTaskId(taskId); + eventBus.post(new DeleteAllSubTaskEvent(taskDO)); + } + + /** + * Processes the given task by determining the services that need to be inserted and removed, + * and performs the corresponding operations. + * + * @param task The task object to be processed. + */ + private void processTask(TaskDO task) { + // Retrieve the set of services for the task, creating a new set if it does not exist + Set serviceSet = subTaskService.computeIfAbsent(task.getTaskId(), k -> ConcurrentHashMap.newKeySet()); + + // Get the list of all service names associated with the task + List serviceNameList = getAllServiceName(task); + + // Determine the services that need to be inserted (those not in the current service set) + List servicesPendingInsertion = serviceNameList.stream() + .filter(serviceName -> !serviceSet.contains(serviceName)) + .map(serviceName -> buildSubTaskDO(task, serviceName)) + .collect(Collectors.toUnmodifiableList()); + + // Determine the services that need to be removed (those in the current service set but not in the service name list) + List servicesPendingRemoval = serviceSet.stream() + .filter(serviceName -> !serviceNameList.contains(serviceName)) + .map(serviceName -> buildSubTaskDO(task, serviceName)) + .collect(Collectors.toUnmodifiableList()); + + + // If all lists are empty, there is nothing to process + if (CollectionUtils.isEmpty(serviceNameList) && CollectionUtils.isEmpty(servicesPendingInsertion) + && CollectionUtils.isEmpty(servicesPendingRemoval)) { + log.debug("No service found for task: {}", task.getTaskId()); + return; + } + + + // If the task status is SYNC, handle the insertion of services + if (TaskStatusEnum.SYNC.getCode().equals(task.getTaskStatus())) { + handleInsertion(servicesPendingInsertion, serviceSet); + } + // Handle the removal of services + handleRemoval(servicesPendingRemoval, serviceSet); + + if (TaskStatusEnum.DELETE.getCode().equals(task.getTaskStatus())) { + List allSubTasks = serviceNameList.stream().map(serviceName -> buildSubTaskDO(task, serviceName)) + .collect(Collectors.toList()); + handleRemoval(allSubTasks, serviceSet); + } + } + + /** + * Handles the insertion of services. + * + * @param servicesPendingInsertion The list of services to be inserted. + * @param serviceSet The set of services. + */ + private void handleInsertion(List servicesPendingInsertion, Set serviceSet) { + BatchTaskExecutor.batchOperation(servicesPendingInsertion, t -> { + eventBus.post(new SyncTaskEvent(t)); + serviceSet.add(t.getServiceName()); + }); + } + /** + * Handles the removal of services. + * + * @param servicesPendingRemoval The list of services to be removed. + * @param serviceSet The set of services. + */ + private void handleRemoval(List servicesPendingRemoval, Set serviceSet) { + BatchTaskExecutor.batchOperation(servicesPendingRemoval, t -> { + eventBus.post(new DeleteTaskEvent(t)); + serviceSet.remove(t.getServiceName()); + }); + } + /** + * Builds a sub-task object for the given task and service name. + * + * @param serviceName The service name. + * @return The constructed sub-task object. + */ + private static TaskDO buildSubTaskDO(TaskDO taskDO, String serviceName) { + TaskDO task = new TaskDO(); + + BeanUtils.copyProperties(taskDO, task); + task.setTaskId(SkyWalkerUtil.generateTaskId(serviceName, taskDO.getGroupName(), taskDO.getSourceClusterId(), + taskDO.getDestClusterId())); + task.setServiceName(serviceName); + return task; + } + + /** + * Retrieves all service names associated with the given task. + * + * @return A list of service names. + */ + private List getAllServiceName(TaskDO taskDO) { + NamingService namingService = nacosServerHolder.get(taskDO.getSourceClusterId()); + if (namingService == null) { + log.warn("naming service is null or not found, clusterId:{}", taskDO.getSourceClusterId()); + return Collections.emptyList(); + } + try { + ListView servicesOfServer = namingService.getServicesOfServer(0, Integer.MAX_VALUE, + taskDO.getGroupName()); + return servicesOfServer.getData(); + } catch (Exception e) { + log.error("query service list failure", e); + } + + return Collections.emptyList(); + } + + + +} diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllThread.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllThread.java deleted file mode 100644 index bd332220..00000000 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllThread.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.nacossync.timer; - -import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.pojo.ListView; -import com.alibaba.nacos.client.naming.utils.CollectionUtils; -import com.alibaba.nacossync.cache.SkyWalkerCacheServices; -import com.alibaba.nacossync.constant.MetricsStatisticsType; -import com.alibaba.nacossync.constant.TaskStatusEnum; -import com.alibaba.nacossync.dao.TaskAccessService; -import com.alibaba.nacossync.extension.holder.NacosServerHolder; -import com.alibaba.nacossync.monitor.MetricsManager; -import com.alibaba.nacossync.pojo.model.TaskDO; -import com.google.common.eventbus.EventBus; -import lombok.extern.slf4j.Slf4j; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * when the database task service name is empty, check all the services in the cluster and create a synchronization task. - * @ClassName: CheckRunningStatusAllThread - * @Author: ChenHao26 - * @Date: 2022/7/20 10:30 - * @Description: muti sync data - */ -@Slf4j -public class CheckRunningStatusAllThread implements Runnable{ - - private MetricsManager metricsManager; - - private SkyWalkerCacheServices skyWalkerCacheServices; - - private TaskAccessService taskAccessService; - - private EventBus eventBus; - - private NacosServerHolder nacosServerHolder; - - private FastSyncHelper fastSyncHelper; - - public CheckRunningStatusAllThread(MetricsManager metricsManager, SkyWalkerCacheServices skyWalkerCacheServices, - TaskAccessService taskAccessService, EventBus eventBus, NacosServerHolder nacosServerHolder, - FastSyncHelper fastSyncHelper) { - this.metricsManager = metricsManager; - this.skyWalkerCacheServices = skyWalkerCacheServices; - this.taskAccessService = taskAccessService; - this.eventBus = eventBus; - this.nacosServerHolder = nacosServerHolder; - this.fastSyncHelper = fastSyncHelper; - } - - /** - * 根据ns级别进行数据同步 - */ - @Override - public void run() { - Long startTime = System.currentTimeMillis(); - try { - List taskDOS = taskAccessService.findServiceNameIsNull() - .stream().filter(t -> t.getStatus() == null || t.getStatus() == 0) - .collect(Collectors.toList()); - if (CollectionUtils.isEmpty(taskDOS)) { - return; - } - for (TaskDO taskDO : taskDOS) { - List serviceNameList = getServiceNameList(taskDO); - if (CollectionUtils.isEmpty(serviceNameList)) { - continue; - } - - //如果是null,证明此时没有处理完成 - List filterService = serviceNameList.stream() - .filter(serviceName -> skyWalkerCacheServices.getFinishedTask(taskDO.getTaskId() + serviceName ) == null) - .collect(Collectors.toList()); - - if (CollectionUtils.isEmpty(filterService)) { - continue; - } - - // 当删除任务后,此时任务的状态为DELETE,不会执行数据同步 - if (TaskStatusEnum.SYNC.getCode().equals(taskDO.getTaskStatus())) { - fastSyncHelper.syncWithThread(taskDO, filterService); - } - } - }catch (Exception e) { - log.warn("CheckRunningStatusThread Exception ", e); - } - metricsManager.record(MetricsStatisticsType.DISPATCHER_TASK, System.currentTimeMillis() - startTime); - } - - /** - * get serviceName list. - * @param taskDO task info - * @return service list or empty list - */ - private List getServiceNameList(TaskDO taskDO) { - NamingService namingService = nacosServerHolder.get(taskDO.getSourceClusterId()); - try { - ListView servicesOfServer = namingService.getServicesOfServer(0, Integer.MAX_VALUE, - taskDO.getGroupName()); - return servicesOfServer.getData(); - } catch (Exception e) { - log.error("query service list failure",e); - } - - return Collections.emptyList(); - } -} diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/FastSyncHelper.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/FastSyncHelper.java deleted file mode 100644 index f379bf1a..00000000 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/FastSyncHelper.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.nacossync.timer; - -import com.alibaba.nacossync.cache.SkyWalkerCacheServices; -import com.alibaba.nacossync.constant.MetricsStatisticsType; -import com.alibaba.nacossync.extension.SyncManagerService; -import com.alibaba.nacossync.monitor.MetricsManager; -import com.alibaba.nacossync.pojo.model.TaskDO; -import com.alibaba.nacossync.util.Tuple; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Consumer; - -import static com.alibaba.nacossync.constant.SkyWalkerConstants.MAX_THREAD_NUM; - -/** - * multi-threaded synchronization Task DO. - * - * @ClassName: FastSyncHelper - * @Author: ChenHao26 - * @Date: 2022/7/19 17:02 - * @Description: - */ -@Service -@Slf4j -public class FastSyncHelper { - - private final SkyWalkerCacheServices skyWalkerCacheServices; - - private final MetricsManager metricsManager; - - private final SyncManagerService syncManagerService; - - - private final ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_NUM); - - public FastSyncHelper(SkyWalkerCacheServices skyWalkerCacheServices, MetricsManager metricsManager, - SyncManagerService syncManagerService) { - this.skyWalkerCacheServices = skyWalkerCacheServices; - this.metricsManager = metricsManager; - this.syncManagerService = syncManagerService; - } - - - /** - * every 200 services start a thread to perform synchronization. - * - * @param taskDOS task list - */ - public void syncWithThread(List taskDOS, Consumer timeSyncInvoke) { - sync(taskDOS, tuple -> { - for (TaskDO task : tuple.getT2()) { - //执行兜底的定时同步 - timeSyncInvoke.accept(task); - } - }); - } - - - /** - * every 200 services start a thread to perform synchronization. - * - * @param taskDO task info - * @param filterServices filterServices - */ - public void syncWithThread(TaskDO taskDO, List filterServices) { - sync(filterServices, tuple -> { - // 执行数据同步 - for (String serviceName : tuple.getT2()) { - syncByIndex(taskDO, serviceName, tuple.getT1()); - } - }); - } - - public void sync(List items, Consumer>> itemConsumer) { - long startTime = System.currentTimeMillis(); - List>> taskGroupList = averageAssign(items, MAX_THREAD_NUM); - - // 等待所有任务完成 - CompletableFuture allTasks = CompletableFuture.allOf(taskGroupList.stream() - .map(tuple -> CompletableFuture.runAsync(() -> performSync(tuple, itemConsumer), executorService)) - .toArray(CompletableFuture[]::new)); - try { - allTasks.get(); - } catch (Exception e) { - e.printStackTrace(); - } - - log.info("新增同步任务数量 {}, 执行耗时:{}ms", items.size(), System.currentTimeMillis() - startTime); - } - - private void performSync(Tuple> tuple, Consumer>> itemConsumer) { - if (tuple == null || tuple.getT2() == null || tuple.getT2().isEmpty()) { - return; - } - itemConsumer.accept(tuple); - - } - - - private void syncByIndex(TaskDO taskDO, String serviceName, int index) { - long startTime = System.currentTimeMillis(); - TaskDO task = new TaskDO(); - BeanUtils.copyProperties(taskDO, task); - task.setServiceName(serviceName); - task.setOperationId(taskDO.getTaskId() + serviceName); - if (syncManagerService.sync(task, index)) { - skyWalkerCacheServices.addFinishedTask(task); - log.info("sync thread : {} sync finish ,time consuming :{}", Thread.currentThread().getId(), - System.currentTimeMillis() - startTime); - metricsManager.record(MetricsStatisticsType.SYNC_TASK_RT, System.currentTimeMillis() - startTime); - } else { - log.warn("listenerSyncTaskEvent sync failure."); - } - } - - /** - * 将一个List均分成n个list,主要通过偏移量来实现的 - * - * @param source 源集合 - * @param limit 最大值 - * @return - */ - public static List>> averageAssign(List source, int limit) { - if (null == source || source.isEmpty()) { - return Collections.emptyList(); - } - int size = source.size(); - List>> result = new ArrayList<>(); - // 通过减去1并加1,我们可以确保将多余的元素放在最后一个子列表中。在上述示例中,计算结果为 ((10 - 1) / 3 + 1) = 4,我们创建了4个子列表,其中最后一个子列表包含2个元素,而不是1个。这样可以更均匀地分配源列表的元素. - int listCount = (int) Math.ceil((double) source.size() / limit); // 计算子列表数量,使用 Math.ceil 向上取整,确保多余的元素放在最后一个子列表中 - int remainder = source.size() % listCount; // 计算多余的元素数量 - int assigned = 0; // 记录已分配的元素索引 - for (int i = 0; i < listCount; i++) { - int sublistSize = size / listCount + (remainder-- > 0 ? 1 : 0); // 计算子列表大小,平均分配元素,并在有多余元素时将其分配到子列表中 - List sublist = source.subList(assigned, assigned + sublistSize); // 获取子列表 - result.add(Tuple.of(i, sublist)); // 将子列表添加到结果列表 - assigned += sublistSize; // 更新已分配的元素索引 - } - - return result; - } - - -} diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/QuerySyncTaskTimer.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/QuerySyncTaskTimer.java index b840ac85..9977043f 100644 --- a/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/QuerySyncTaskTimer.java +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/timer/QuerySyncTaskTimer.java @@ -22,7 +22,6 @@ import com.alibaba.nacossync.dao.TaskAccessService; import com.alibaba.nacossync.event.DeleteTaskEvent; import com.alibaba.nacossync.event.SyncTaskEvent; -import com.alibaba.nacossync.extension.SyncManagerService; import com.alibaba.nacossync.extension.holder.NacosServerHolder; import com.alibaba.nacossync.monitor.MetricsManager; import com.alibaba.nacossync.pojo.model.TaskDO; @@ -32,6 +31,7 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Service; +import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -59,12 +59,6 @@ public class QuerySyncTaskTimer implements CommandLineRunner { @Autowired private NacosServerHolder nacosServerHolder; - - @Autowired - private SyncManagerService syncManagerService; - - @Autowired - private FastSyncHelper fastSyncHelper; @Override public void run(String... args) { @@ -72,8 +66,8 @@ public void run(String... args) { scheduledExecutorService.scheduleWithFixedDelay(new CheckRunningStatusThread(), 0, 3000, TimeUnit.MILLISECONDS); - scheduledExecutorService.scheduleWithFixedDelay(new CheckRunningStatusAllThread(metricsManager,skyWalkerCacheServices, - taskAccessService,eventBus, nacosServerHolder, fastSyncHelper), 0, 3000, + scheduledExecutorService.scheduleWithFixedDelay(new CheckRunningStatusAllNacosThread(metricsManager, + taskAccessService, nacosServerHolder, eventBus), 0, 3000, TimeUnit.MILLISECONDS); } @@ -85,7 +79,7 @@ public void run() { Long start = System.currentTimeMillis(); try { - Iterable taskDOS = taskAccessService.findAll(); + List taskDOS = taskAccessService.findAllByServiceNameNotEqualAll(); taskDOS.forEach(taskDO -> { diff --git a/nacossync-worker/src/main/java/com/alibaba/nacossync/util/BatchTaskExecutor.java b/nacossync-worker/src/main/java/com/alibaba/nacossync/util/BatchTaskExecutor.java new file mode 100644 index 00000000..74dce015 --- /dev/null +++ b/nacossync-worker/src/main/java/com/alibaba/nacossync/util/BatchTaskExecutor.java @@ -0,0 +1,76 @@ +package com.alibaba.nacossync.util; + +import com.alibaba.nacos.client.naming.utils.CollectionUtils; +import com.alibaba.nacossync.pojo.model.TaskDO; +import com.google.common.base.Stopwatch; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +@Slf4j +public class BatchTaskExecutor { + + private static final int MAX_THREAD_NUM = 200; + private static final ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_NUM); + + /** + * 批量操作方法 + * + * @param items 任务列表 + * @param operation 要执行的操作 + */ + public static void batchOperation(List items, Consumer operation) { + Stopwatch stopwatch = Stopwatch.createStarted(); + + List>> taskGroupList = averageAssign(items, MAX_THREAD_NUM); + + // 创建一个包含所有任务的 CompletableFuture + CompletableFuture[] futures = taskGroupList.stream().map(tuple -> CompletableFuture.runAsync(() -> { + for (TaskDO taskDO : tuple.getT2()) { + operation.accept(taskDO); + } + }, executorService)).toArray(CompletableFuture[]::new); + + try { + // 等待所有任务完成 + CompletableFuture.allOf(futures).join(); + } catch (Exception e) { + log.error("Error occurred during sync operation", e); + } + + log.debug("Total sync tasks: {}, Execution time: {} ms", items.size(), stopwatch.elapsed(TimeUnit.MILLISECONDS)); + } + + /** + * 将一个List均分成n个list, 主要通过偏移量来实现的 + * + * @param source 源集合 + * @param limit 最大值 + * @return 均分后的列表 + */ + private static List>> averageAssign(List source, int limit) { + if (CollectionUtils.isEmpty(source)) { + return Collections.emptyList(); + } + + int size = source.size(); + int listCount = (int) Math.ceil((double) size / limit); // Calculate the number of sublist + int remainder = size % listCount; // Calculate the number of remaining elements after even distribution + List>> result = new ArrayList<>(listCount); // Initialize the result list with the expected size + + for (int i = 0, assigned = 0; i < listCount; i++) { + int sublistSize = size / listCount + (remainder-- > 0 ? 1 : 0); // Determine the size of each sublist, distribute remaining elements + List sublist = new ArrayList<>(source.subList(assigned, assigned + sublistSize)); // Create the sublist + result.add(Tuple.of(i, sublist)); // Add the sublist to the result + assigned += sublistSize; // Update the assigned index + } + + return result; + } +} \ No newline at end of file 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..a597961f 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 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 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 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 list = new ArrayList<>(); - list.add(taskDO); - try { - fastSyncHelper.syncWithThread(list, nacosSyncToNacosService::timeSync); - } catch (Exception e) { - Assert.assertEquals(e, InterruptedException.class); - e.printStackTrace(); - } - } - - @Test - public void testAverageAssign() { - int limit = 2; - List sourceList = new ArrayList<>(); - sourceList.add("1"); - sourceList.add("2"); - sourceList.add("3"); - List>> lists = FastSyncHelper.averageAssign(sourceList, limit); - Assert.assertEquals(lists.get(0).getT2().size(), limit); - Assert.assertNotEquals(lists.get(0).getT2().size(), 3); - } -} diff --git a/pom.xml b/pom.xml index 539b3a45..824aa8ff 100644 --- a/pom.xml +++ b/pom.xml @@ -25,19 +25,18 @@ pom - 0.4.9 - 2.5.14 - 2020.0.2 + 0.5.0 + 2.7.17 + 2021.0.3 UTF-8 - 3.4.9 - 4.1.0 + 3.4.14 + 4.3.0 1.10.19 - 1.4.2 + 1.4.7 1.18.2 - 3.0.0 1.3.1 3.12.0 - 30.1-jre + 33.2.0-jre 2.2 11 11 @@ -57,6 +56,7 @@ 1.0.2 1.0-beta-4 1.7.17 + 1.8.0 @@ -110,14 +110,9 @@ test - io.springfox - springfox-swagger2 - ${swagger.verison} - - - io.springfox - springfox-swagger-ui - ${swagger.verison} + org.springdoc + springdoc-openapi-ui + ${swagger.version} com.alibaba.nacos