From 8185feca1da4627b312d163db6d94badc2f5cd0b Mon Sep 17 00:00:00 2001 From: paderlol Date: Sun, 19 May 2024 00:17:01 +0800 Subject: [PATCH] 0.5.0 (#352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat/sync support2.x#mutiple thread sync02 (#304) * update port * Multithreading sync * solve conflict * imple SyncService * adapter deregister * optimization some code * fix deregister instance equals logic Co-authored-by: Oliver Co-authored-by: paderlol * Optimize the code for assigning tasks. (#320) * Develop (#321) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix #305 (#322) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code (#323) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic (#324) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic. * #346 #350:nacos 2 nacos 同步关闭后心跳没有停止问题 ,nacos-sync删除目标节点问题 (#347) * update port * fix #297 (#298) Co-authored-by: yangchun2 * Revert "fix #297 (#298)" (#318) This reverts commit a9df169b5adac48a9a94e437798175cdd38ad673. * 0.4.9-pre (#325) * Feat/sync support2.x#mutiple thread sync02 (#304) * update port * Multithreading sync * solve conflict * imple SyncService * adapter deregister * optimization some code * fix deregister instance equals logic Co-authored-by: Oliver Co-authored-by: paderlol * Optimize the code for assigning tasks. (#320) * Develop (#321) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix #305 (#322) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code (#323) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic (#324) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic. --------- Co-authored-by: chenhao26 <35129699+chenhao26-nineteen@users.noreply.github.com> Co-authored-by: Oliver * fix:nacos 2 nacos 同步关闭后心跳没有停止问题 * fix:nacos 2 nacos 同步关闭后心跳没有停止问题 * fix:集群信息保存 cluster_level 为 null * fix: 1、注册时根据中心化逻辑判断同步,但是删除时逻辑不一致问题 2、如果停止同步nameservice为空问题 * 问题在于对destInstances列表的更新方式。在Java中,方法参数是按值传递的。这意味着当你传递一个对象到方法中时,实际上传递的是对象引用的副本。因此,如果你在方法内部改变了这个引用指向的对象(例如,将其指向一个新的对象),这个改变不会影响到原始的对象引用。 在你的代码中,destInstances = newDestInstance;这一行只是改变了destInstances引用在方法内部的指向,而不会改变方法外部传入的destInstances列表对象。这意味着,尽管你筛选出了需要反注册的实例,但这个改变不会反映到方法调用者那里。 --------- Co-authored-by: Oliver Co-authored-by: paderlol Co-authored-by: 杨春 Co-authored-by: yangchun2 Co-authored-by: chenhao26 <35129699+chenhao26-nineteen@users.noreply.github.com> * 0.5.0 (#351) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic. * 1. 重新设计全量 Nacos 同步 Nacos 2. 修复Nacos Instance equals无效导致出现无法注册成功问题 3. 升级Nacos Sync JDK/Spring Boot版本 4. 保底同步从改成并发同步 5. 增加部分注释 * Develop (#353) * Optimize the code for assigning tasks. * Adds prefix to the input string if it doesn't already have it.#308 * Fix .#305 * Fix cyclic dependency code. * Refactoring the Nacos Sync to Consul Logic. * 1. 重新设计全量 Nacos 同步 Nacos 2. 修复Nacos Instance equals无效导致出现无法注册成功问题 3. 升级Nacos Sync JDK/Spring Boot版本 4. 保底同步从改成并发同步 5. 增加部分注释 --------- Co-authored-by: chenhao26 <35129699+chenhao26-nineteen@users.noreply.github.com> Co-authored-by: Oliver Co-authored-by: 龙竹 <34528665+dragonTalon@users.noreply.github.com> Co-authored-by: 杨春 Co-authored-by: yangchun2 --- README.md | 16 +- img_1.png | Bin 0 -> 13173 bytes img_2.png | Bin 0 -> 13405 bytes nacossync-worker/pom.xml | 17 +- .../com/alibaba/nacossync/NacosSyncMain.java | 3 - .../cache/SkyWalkerCacheServices.java | 17 +- .../constant/SkyWalkerConstants.java | 2 +- .../nacossync/dao/TaskAccessService.java | 11 +- .../dao/repository/TaskRepository.java | 11 +- .../event/DeleteAllSubTaskEvent.java | 13 + .../event/listener/EventListener.java | 18 +- .../extension/SyncManagerService.java | 16 +- .../extension/client/SyncQueryClient.java | 3 +- .../holder/AbstractServerHolderImpl.java | 7 +- .../extension/holder/NacosServerHolder.java | 78 +-- .../extension/impl/AbstractNacosSync.java | 120 +++-- .../impl/NacosSyncToNacosServiceImpl.java | 456 +++++++++--------- .../impl/NacosSyncToZookeeperServiceImpl.java | 85 ++-- .../impl/ZookeeperSyncToNacosServiceImpl.java | 321 +++++++----- .../alibaba/nacossync/pojo/model/TaskDO.java | 4 - .../processor/ClusterAddProcessor.java | 3 +- .../processor/TaskDeleteProcessor.java | 34 +- .../processor/TaskUpdateProcessor.java | 24 +- .../CheckRunningStatusAllNacosThread.java | 252 ++++++++++ .../timer/CheckRunningStatusAllThread.java | 126 ----- .../nacossync/timer/FastSyncHelper.java | 169 ------- .../nacossync/timer/QuerySyncTaskTimer.java | 14 +- .../nacossync/util/BatchTaskExecutor.java | 76 +++ .../main/resources/static/js/main.b73436b5.js | 2 +- .../impl/NacosSyncToNacosServiceImplTest.java | 34 +- .../nacossync/timer/FastSyncHelperTest.java | 90 ---- pom.xml | 27 +- 32 files changed, 1026 insertions(+), 1023 deletions(-) create mode 100644 img_1.png create mode 100644 img_2.png create mode 100644 nacossync-worker/src/main/java/com/alibaba/nacossync/event/DeleteAllSubTaskEvent.java create mode 100644 nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllNacosThread.java delete mode 100644 nacossync-worker/src/main/java/com/alibaba/nacossync/timer/CheckRunningStatusAllThread.java delete mode 100644 nacossync-worker/src/main/java/com/alibaba/nacossync/timer/FastSyncHelper.java create mode 100644 nacossync-worker/src/main/java/com/alibaba/nacossync/util/BatchTaskExecutor.java delete mode 100644 nacossync-worker/src/test/java/com/alibaba/nacossync/timer/FastSyncHelperTest.java 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 0000000000000000000000000000000000000000..eee07444d74ac65ed65c416eec7bef8e7a8d49c5 GIT binary patch literal 13173 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 literal 0 HcmV?d00001 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