Compare commits

...

321 Commits

Author SHA1 Message Date
paderlol 0c35e096c2
Merge pull request #384 from nacos-group/develop
Optimize some codes
2024-11-30 15:23:35 +08:00
paderlol 142a36f608
Merge pull request #383 from paderlol/develop
Optimize some codes
2024-11-30 15:22:07 +08:00
paderlol b18afb95c1
Optimize some codes 2024-11-30 15:20:40 +08:00
paderlol aa87b24e1a
Merge pull request #381 from nacos-group/develop
Develop
2024-10-29 10:55:13 +08:00
paderlol fdc51ba605
Merge pull request #380 from paderlol/develop
Write unit tests for all utils classes
2024-10-29 10:53:56 +08:00
paderlol ce6249142f
Write unit tests for all utils classes 2024-10-29 10:52:01 +08:00
paderlol 26c8b307d7
Merge pull request #378 from nacos-group/develop
Develop
2024-09-28 13:29:35 +08:00
paderlol 6585b217bc
Merge pull request #377 from paderlol/develop
1. 对代码中依赖注入方式全部修改成构造函数注入
2024-09-28 13:28:32 +08:00
paderlol b609c729c3
1. 对代码中依赖注入方式全部修改成构造函数注入 2024-09-28 13:26:07 +08:00
paderlol 432ced4e0e
Merge pull request #375 from paderlol/develop
优化部分代码
2024-08-29 16:49:31 +08:00
paderlol 116116c8c4
1. 优化部分集群新增部分冗余代码
2. 升级Java JDK到17
2024-08-29 16:48:58 +08:00
paderlol ca08bd7d6e
1. 优化部分集群新增部分冗余代码
2. 升级Java JDK到17
2024-08-29 16:43:05 +08:00
paderlol 81610e505a
Merge pull request #373 from paderlol/add_validation_cluster
修复有任务运行时可以删除集群信息的问题 #371
2024-07-31 15:57:00 +08:00
paderlol 17439be724
修复有任务运行时可以删除集群信息的问题 #371 2024-07-31 15:45:05 +08:00
paderlol 91999fbdcb
Merge pull request #368 from nacos-group/develop
优化以及修复小错误
2024-07-20 16:44:16 +08:00
paderlol 6645b00f24
Develop (#367)
* 1. 优化部分代码

* 整理代码:
 1. 移除枚举类冗余方法以及SkyWalkerCacheServices中未使用方法
 2. 部分服务类改用构造注入
 3. Entity对象重写equals和hashCode
2024-07-20 16:33:19 +08:00
paderlol acb7b49551
Merge pull request #360 from MajorHe1/patch-1
[#ISSUE 359]Update SkyWalkerCacheServices.java, fix operationId empty-judge problem
2024-07-20 14:26:31 +08:00
MajorHe1 ba416d1496
[#ISSUE 359]Update SkyWalkerCacheServices.java, fix operationId empty-judge problem 2024-06-13 20:05:49 +08:00
paderlol ff5e484997
Merge pull request #357 from nacos-group/develop
Develop
2024-06-10 22:19:41 +08:00
paderlol b900c28347
1. 优化部分代码 (#356) 2024-06-10 22:18:13 +08:00
paderlol 5d17242ffc
Develop (#355)
* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <yangchun2@joyy.com>

* Revert "fix #297 (#298)" (#318)

This reverts commit a9df169b5a.

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <wqdyxnbd@163.com>

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>
Co-authored-by: 杨春 <chun@kpromise.top>
Co-authored-by: yangchun2 <yangchun2@joyy.com>
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. 增加部分注释

* 1. 优化部分代码 (#354)

* 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. 增加部分注释

* 1. 优化部分代码

---------

Co-authored-by: chenhao26 <35129699+chenhao26-nineteen@users.noreply.github.com>
Co-authored-by: Oliver <wqdyxnbd@163.com>
Co-authored-by: 龙竹 <34528665+dragonTalon@users.noreply.github.com>
Co-authored-by: 杨春 <chun@kpromise.top>
Co-authored-by: yangchun2 <yangchun2@joyy.com>
2024-06-10 22:04:01 +08:00
paderlol 39357660ab
Merge branch 'master' into develop 2024-06-10 22:02:39 +08:00
paderlol 49abb7c286
1. 优化部分代码 (#354)
* 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. 增加部分注释

* 1. 优化部分代码
2024-06-10 22:01:12 +08:00
paderlol 8185feca1d
0.5.0 (#352)
* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <yangchun2@joyy.com>

* Revert "fix #297 (#298)" (#318)

This reverts commit a9df169b5a.

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <wqdyxnbd@163.com>

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>
Co-authored-by: 杨春 <chun@kpromise.top>
Co-authored-by: yangchun2 <yangchun2@joyy.com>
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 <wqdyxnbd@163.com>
Co-authored-by: 龙竹 <34528665+dragonTalon@users.noreply.github.com>
Co-authored-by: 杨春 <chun@kpromise.top>
Co-authored-by: yangchun2 <yangchun2@joyy.com>
2024-05-19 00:17:01 +08:00
paderlol 5789d4d774
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. 增加部分注释
2024-05-19 00:15:50 +08:00
paderlol 2fdf44d308
Merge branch 'master' into develop 2024-05-19 00:08:04 +08:00
paderlol 5941bfb9bc
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. 增加部分注释
2024-05-18 23:50:23 +08:00
龙竹 0c24dcb0a2
#346 #350:nacos 2 nacos 同步关闭后心跳没有停止问题 ,nacos-sync删除目标节点问题 (#347)
* update port

* fix #297 (#298)

Co-authored-by: yangchun2 <yangchun2@joyy.com>

* Revert "fix #297 (#298)" (#318)

This reverts commit a9df169b5a.

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <wqdyxnbd@163.com>

* 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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>
Co-authored-by: 杨春 <chun@kpromise.top>
Co-authored-by: yangchun2 <yangchun2@joyy.com>
Co-authored-by: chenhao26 <35129699+chenhao26-nineteen@users.noreply.github.com>
2024-05-15 09:53:45 +08:00
paderlol a1d683a7c9
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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>

* 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 <wqdyxnbd@163.com>
2023-05-15 17:33:26 +08:00
paderlol be65db1892
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.
2023-05-15 17:25:22 +08:00
paderlol eb93fdd52a
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.
2023-05-15 17:21:53 +08:00
paderlol e7e52acfa7
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
2023-05-15 16:51:11 +08:00
paderlol 02838da959
Develop (#321)
* Optimize the code for assigning tasks.

* Adds prefix to the input string if it doesn't already have it.#308
2023-05-15 16:13:39 +08:00
paderlol 0e73a0864c
Optimize the code for assigning tasks. (#320) 2023-05-15 15:58:39 +08:00
paderlol 61d347609e
Revert "fix #297 (#298)" (#318)
This reverts commit a9df169b5a.
2023-05-15 09:55:50 +08:00
杨春 a9df169b5a
fix #297 (#298)
Co-authored-by: yangchun2 <yangchun2@joyy.com>
2023-05-15 09:53:25 +08:00
chenhao26 98efe26906
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 <wqdyxnbd@163.com>
Co-authored-by: paderlol <huangmnlove@163.com>
2022-08-01 08:53:33 +08:00
paderlol 384a9cc1bb
Merge pull request #303 from nacos-group/develop
0.4.8
2022-07-30 12:47:09 +08:00
paderlol 0ffdca3e0b
Merge pull request #302 from paderlol/develop
Optimize Nacos synchronization Nacos logic.
2022-07-30 12:18:18 +08:00
paderlol 5ae4071508 Optimize Nacos synchronization Nacos logic. 2022-07-30 12:15:56 +08:00
paderlol 4082efa1fc
Merge pull request #301 from paderlol/develop
Optimize Nacos synchronization Eureka logic.
2022-07-30 11:49:28 +08:00
paderlol fc03e6483c Optimize Nacos synchronization Eureka logic. 2022-07-30 11:46:46 +08:00
paderlol 2b8d29a0ab
Merge pull request #296 from jefferson-chern/fix-resync-when-two-way-sync
[fix #295] 双向同步时,重启某个集群下所有实例,可以重新同步
2022-06-14 21:00:22 +08:00
jefferson 881c11d51f refactor code: remove some useless code. 2022-06-14 16:14:07 +08:00
jefferson 855922964e fix #295 2022-06-14 14:59:12 +08:00
paderlol 9e814d2d43
Merge pull request #294 from paderlol/develop
Refactor some code
2022-06-08 21:36:08 +08:00
paderlol cdfbebf407 Refactor some code 2022-06-08 21:34:13 +08:00
paderlol 017cacf12c
Merge pull request #293 from jefferson-chern/fix-log-bug-and-refactor-version-and-upgrade-to-0.4.8
统一版本号;升级到0.4.8;修复日志不删除问题
2022-06-07 13:52:52 +08:00
jefferson ab4a4bbb92 1. dependency的版本号全部挪到父pom的dependencyManagement;
2. plugin的版本号全部挪到父pom的pluginManagement;
3. nacosSync名称修改为nacos-sync;
4. 父pom和子pom的version统一由revision管理,只需一处修改;
5. 修复一些readme.md中的明显错误;
6. 修复日志文件永不删除的问题;
7. upgrade version to 0.4.8;
8. remove some useless newline in redeme.md.
2022-06-05 15:28:04 +08:00
paderlol 853119b615
Merge pull request #289 from CherishCai/feat_TaskAddAll_with_nacos_source
Feat task add all with nacos source
2022-06-04 19:42:15 +08:00
paderlol f99398c649
Merge pull request #290 from Oliverwqcwrw/master-update-port-1
Update README.md port
2022-04-20 09:59:44 +08:00
Oliver a61a79aca9 update port 2022-04-20 09:02:22 +08:00
hongwen.chw 00570523fa feat: 支持从 sourceCluster 获取所有 service,然后生成同步到 destCluster 的任务。// 目前仅支持 Nacos 为源的同步类型,待完善更多类型支持. 2022-03-23 20:14:52 +08:00
paderlol 508a912c35
Merge pull request #281 from nacos-group/develop
Develop
2021-11-21 17:42:22 +08:00
paderlol 3ee0d33fff
Merge branch 'master' into develop 2021-11-21 17:42:13 +08:00
paderlol 03e4419157
Merge pull request #275 from DavidLei08/fix-257-namespace
Fix 257 namespace
2021-11-01 09:29:15 +08:00
paderlol 3998dfedf1
Merge pull request #276 from paderlol/develop
Add basic auth to Nacos cluster
2021-10-12 18:03:51 +08:00
paderlol 1ffec6a785 Add basic auth to Nacos cluster 2021-10-12 18:02:44 +08:00
DavidLei08 0fad6ef8cc 增加window bat启动脚本 2021-09-29 23:05:31 +08:00
DavidLei08 46b3590fb6 namespace同步无效问题 2021-09-29 22:32:40 +08:00
DavidLei08 a2d870e15e 解决 consul同步到nacos namespace无效的问题 2021-09-29 22:23:16 +08:00
paderlol 758b18cb7d
Merge pull request #274 from paderlol/develop
Fixed Nacos resync invalid issue #272
2021-09-03 12:04:44 +08:00
paderlol d630fb16b4 Fixed Nacos resync invalid issue #272 2021-09-03 12:04:10 +08:00
paderlol 7c6f889e72
Merge pull request #273 from paderlol/develop
Add Nacos Cluster Specify Namespace Features.#272
2021-08-31 20:05:57 +08:00
paderlol 0826dc9c15 Add Nacos Cluster Specify Namespace Features.#272 2021-08-31 20:05:15 +08:00
paderlol 96111de3da
Merge pull request #271 from zyhui98/feature/cluster-sql
feat: cluster表初始化sql脚本缺少字段
2021-08-18 22:43:17 +08:00
zhuyunhui 137fdee7f1 feat: cluster表初始化sql脚本缺少字段 2021-08-18 21:11:36 +08:00
paderlol c4200d7516
Merge pull request #268 from paderlol/develop
Add Eureka sync supports group
2021-08-11 17:14:04 +08:00
paderlol 30b331b4b0 Add Eureka sync Nacos by group 2021-08-11 17:11:07 +08:00
paderlol 8fceffde07
Merge pull request #267 from zrlw/patch-syncNewInstance
add tap control for same sync task
2021-08-05 07:14:44 +08:00
zrlw 8dde539a79 remove useless Date code and correct comment 2021-08-05 00:30:18 +08:00
zrlw 1cd2e3b408 add tap control for same sync task 2021-08-04 19:46:49 +08:00
paderlol ee3040f011
Merge pull request #263 from xyohn/develop
#262 修复Eureka<->Nacos同步问题
2021-07-28 20:43:01 +08:00
xYohn c5d574e357 #262 Fix NacosSyncToEureka Support Nacos NameSpace 2021-07-28 11:13:41 +08:00
xYohn eb3e74a678 #262 Fix EurekaSyncToNacos Support Nacos NameSpace 2021-07-28 11:13:25 +08:00
paderlol 1f16efb043
Merge pull request #260 from paderlol/develop
Fix issue #259
2021-07-24 11:11:32 +08:00
paderlol 467752e355 Fix issue #259 2021-07-24 11:10:32 +08:00
paderlol 91bf0d93f8
Merge pull request #255 from zrlw/patch-addBasicSyncThread
add basic sync task thread
2021-06-30 16:35:23 +08:00
zrlw 9cab680e46 add basic sync task thread 2021-06-30 15:23:04 +08:00
paderlol c62e93a677
Merge pull request #247 from paderlol/develop
Nacos client version temporarily returned to 1.4.2.
2021-06-29 15:58:23 +08:00
paderlol e3354fefb0 Nacos client version temporarily returned to 1.4.2. 2021-06-29 14:31:37 +08:00
paderlol 57e03c8aee
Merge pull request #246 from zrlw/patch-EventListener
do not add FinishedTask if syncManagerService return false,  add group parameter for getAllInstances at sync and only sync healthy instance
2021-06-29 14:02:29 +08:00
paderlol f1c1c00190
Merge pull request #242 from JerryGuTripleUp/develp-Jerry
Update main.cf091959.js
2021-06-29 14:01:21 +08:00
zrlw 4fced18886 only sync healthy instance 2021-06-29 13:02:11 +08:00
zrlw bdfa05670e add group parameter for getAllInstances at sync 2021-06-29 12:17:27 +08:00
zrlw d389587d96 do not add FinishedTask if syncManagerService return false 2021-06-29 12:16:51 +08:00
JerryGuTripleUp e5283d2a23 Update main.cf091959.js 2021-06-23 16:31:47 +08:00
paderlol e65fba55a0 Add link of the example into README. 2021-05-20 17:09:03 +08:00
paderlol b17f6fff8d Upgrade version 2.0.1 of the Nacos client. 2021-05-16 17:15:31 +08:00
paderlol 8748f0c2be Fix unit test 2021-04-20 15:43:55 +08:00
paderlol c38eed392d Fix didn't clear snapshot in Nacos sync Nacos issue #224 2021-04-20 15:34:04 +08:00
paderlol 070ac65f78
Merge pull request #218 from fanyanming2016/develop
支持指定namespace数据同步
2021-03-09 13:43:59 +08:00
fanyanming a48db6ec63 支持指定namespace数据同步 2021-03-09 11:40:28 +08:00
paderlol a6f8708aa0
Merge pull request #215 from shalk/patch-2
Update nacosSync.sql
2021-02-26 17:21:40 +08:00
shalk(xiao kun) 98fad6eb9d Update nacosSync.sql
use InnoDB instead of MyISAM  in common case.
2021-02-26 17:19:17 +08:00
paderlol 0c1f7de7dc
Merge pull request #212 from paderlol/develop
Fix delete sync task issue #168
2021-02-25 22:25:39 +08:00
paderlol 5c1aea3681 Fix delete sync task issue #168 2021-02-25 22:21:14 +08:00
paderlol 1b39a45f6c
Merge pull request #209 from paderlol/develop
Fix delete sync task issue #168
2021-02-22 23:28:05 +08:00
paderlol 909c482228 Fix delete sync task issue #168 2021-02-22 23:26:13 +08:00
paderlol a4e453b1fd
Merge pull request #208 from nacos-group/revert-194-develop
Revert "优化nacos-servercluster模式下偶发获取不了instance问题"
2021-02-11 21:15:09 +08:00
paderlol 95e535ff84
Revert "优化nacos-servercluster模式下偶发获取不了instance问题" 2021-02-11 21:14:43 +08:00
paderlol bc98df3c15
Merge pull request #194 from cdmaji/develop
优化nacos-servercluster模式下偶发获取不了instance问题
2021-02-11 21:10:44 +08:00
paderlol 247d0ff08c
Merge pull request #206 from paderlol/develop
#177 #205
2021-02-11 21:09:32 +08:00
paderlol d4b0335b50 Merge remote-tracking branch 'remote/develop' into develop 2021-02-11 21:02:48 +08:00
paderlol ef88f2201c Fix delete sync task issue #168 2021-02-11 21:02:10 +08:00
maj 57e0c00ca5 修复nacos-servercluster模式下偶发获取不了instance问题 2020-12-18 18:42:10 +08:00
paderlol ad8d7aede4
Merge pull request #185 from paderlol/develop
push latest code
2020-11-10 22:17:29 +08:00
paderlol 83903aca2a Merge branch 'master' of https://github.com/nacos-group/nacos-sync 2020-11-10 22:10:52 +08:00
paderlol ba7be8fff0
Merge pull request #166 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/elliptic-6.5.3
chore(deps): bump elliptic from 6.4.1 to 6.5.3 in /nacossync-console/src/main/resources/static/console-fe
2020-11-10 22:09:50 +08:00
paderlol 1d38518d82
Merge pull request #184 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/dot-prop-4.2.1
chore(deps): bump dot-prop from 4.2.0 to 4.2.1 in /nacossync-console/src/main/resources/static/console-fe
2020-11-10 22:09:38 +08:00
dependabot[bot] 860a892391
chore(deps): bump dot-prop
Bumps [dot-prop](https://github.com/sindresorhus/dot-prop) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/sindresorhus/dot-prop/releases)
- [Commits](https://github.com/sindresorhus/dot-prop/compare/v4.2.0...v4.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-10 14:09:20 +00:00
paderlol a0da72a631
Merge pull request #169 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/node-sass-4.13.1
chore(deps-dev): bump node-sass from 4.11.0 to 4.13.1 in /nacossync-console/src/main/resources/static/console-fe
2020-11-10 22:08:00 +08:00
paderlol e166342476
Merge pull request #153 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/websocket-extensions-0.1.4
chore(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /nacossync-console/src/main/resources/static/console-fe
2020-11-10 22:07:21 +08:00
paderlol edfb25e035
Merge pull request #170 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/http-proxy-1.18.1
chore(deps): bump http-proxy from 1.17.0 to 1.18.1 in /nacossync-console/src/main/resources/static/console-fe
2020-11-10 22:06:37 +08:00
paderlol 6994fcde13
Merge pull request #183 from paderlol/develop
Develop
2020-11-10 22:05:41 +08:00
paderlol 03e307bb1a Merge branch 'master' of https://github.com/nacos-group/nacos-sync 2020-11-10 21:57:15 +08:00
paderlol b386a5a140
Merge pull request #182 from nacos-group/revert-181-master
Revert "排除dubbo下面的所有非服务节点(dubbo v2.6+)"
2020-11-10 21:55:24 +08:00
paderlol 1cf0a7f1ee
Revert "排除dubbo下面的所有非服务节点(dubbo v2.6+)" 2020-11-10 21:54:40 +08:00
paderlol b353bd657d
Merge pull request #181 from cdmaji/master
排除dubbo下面的所有非服务节点(dubbo v2.6+)
2020-11-10 21:51:31 +08:00
maj 256800ecc1 排除dubbo下面的所有非服务节点(dubbo v2.6+) 2020-11-06 16:14:19 +08:00
paderlol e3dba5e920
Merge pull request #179 from nidonglin/master
fixbug:监听zk变化后获取到event的data的path路径包含服务详细信息,正则表达式缺少后缀模糊匹配
2020-10-15 19:25:58 +08:00
donglin.ni 7f33ea54f1 fixbug:监听zk变化后获取到event的data的path路径包含服务详细信息,正则表达式缺少后缀模糊匹配 2020-10-15 18:54:31 +08:00
paderlol 1225addc02
Merge pull request #178 from SeanCool/patch-1
Update NacosSyncToConsulServiceImpl.java
2020-10-15 17:33:49 +08:00
SeanCool 1661721f91
Update NacosSyncToConsulServiceImpl.java
consul反注册异常,添加URL转义
2020-10-15 17:23:06 +08:00
paderlol d89e8be910
Merge pull request #176 from nacos-group/develop
Develop
2020-10-02 17:08:00 +08:00
paderlol 88832f62a6
Merge pull request #175 from paderlol/develop
Fix some issue
2020-10-02 17:06:45 +08:00
paderlol 2a3cc7e7ac Fix delete sync task issue #168 2020-10-02 17:04:53 +08:00
paderlol b8dc1cfc2d Fix Eureka sync to Nacos issue #173 2020-10-02 17:04:05 +08:00
dependabot[bot] a1c131464e
chore(deps): bump http-proxy
Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1.
- [Release notes](https://github.com/http-party/node-http-proxy/releases)
- [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-10 02:00:37 +00:00
dependabot[bot] e777c60dae
chore(deps-dev): bump node-sass
Bumps [node-sass](https://github.com/sass/node-sass) from 4.11.0 to 4.13.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.11.0...v4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 15:43:24 +00:00
dependabot[bot] aa55f6f10b
chore(deps): bump elliptic
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.4.1 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.4.1...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-31 05:54:50 +00:00
pader f1ac50cd50
Merge pull request #165 from nacos-group/develop
0.4.0
2020-07-25 01:04:25 +08:00
pader 76ea013045
Merge pull request #164 from paderlol/develop
Add Nacos cluster into client
2020-07-25 00:56:57 +08:00
paderlol 1daaa87c88 Add Nacos cluster into client 2020-07-25 00:30:10 +08:00
pader 98c724e960
Merge pull request #163 from benyamin2014/master
添加zookeeper全量同步功能 #76
2020-07-24 22:17:25 +08:00
benyamin b9a2cc4269 支持zookeeper向nacos全量同步 2020-07-24 15:13:27 +08:00
benyamin 41743e6eac 支持zookeeper向nacos全量同步 2020-07-24 15:13:06 +08:00
pader 6c19464e10
Merge pull request #162 from nacos-group/develop
Develop
2020-07-21 17:46:56 +08:00
pader 457bb60ae2
Merge pull request #161 from paderlol/develop
Fix issue #160 about the metadata of the Eureka
2020-07-21 17:41:22 +08:00
paderlol 87a3d8664f Fix issue #160 about the metadata of the Eureka 2020-07-21 17:39:37 +08:00
dependabot[bot] 4a1e547f22
chore(deps): bump websocket-extensions
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-06 23:46:32 +00:00
yanlinly 2d514b5964
Update README.md 2020-06-02 20:33:46 +08:00
pader 5b05f5089e
Merge pull request #152 from nacos-group/develop
Release 0.3.8
2020-05-31 18:01:04 +08:00
pader 7c6de38711
Merge pull request #151 from paderlol/develop
Develop
2020-05-31 18:00:09 +08:00
zhanglong 896021a3a6 fix merge conflict 2020-05-31 17:55:09 +08:00
zhanglong 03c6d9fcbd Merge branch 'develop' of https://github.com/nacos-group/nacos-sync into develop
 Conflicts:
	nacossync-worker/src/main/java/com/alibaba/nacossync/api/TaskApi.java
2020-05-31 17:50:50 +08:00
zhanglong e7886fa195 fix some issue for the consul sync 2020-05-31 17:43:57 +08:00
pader acabacce74
Merge pull request #147 from nacos-group/develop
Release 0.3.7
2020-04-26 23:12:19 +08:00
pader a414608331
Merge pull request #146 from paderlol/develop
Release 0.3.7
2020-04-26 23:11:09 +08:00
zhanglong a4437a25a1 Release 0.3.7 2020-04-26 23:07:44 +08:00
pader 9aa2d1f551
Merge pull request #141 from paderlol/develop
Develop
2020-04-11 22:22:00 +08:00
pader dd65185cf1
Merge pull request #96 from yongchao9/feature_batch_delete_task#89
Feature batch delete task#89
2020-04-11 22:08:56 +08:00
zhanglong 664debbab0 The Eureka Sync bug is fixed by zhayujie 2020-04-11 22:05:04 +08:00
pader daf093574c
Merge pull request #137 from paderlol/develop
Optimize code and fix test unit
2020-04-08 18:15:16 +08:00
pader.zhang 4e1f60fe7c Optimize code and fix test unit 2020-04-08 18:14:25 +08:00
pader 8c36d5f82e
Merge pull request #135 from nacos-group/dependabot/npm_and_yarn/nacossync-console/src/main/resources/static/console-fe/acorn-5.7.4
chore(deps): bump acorn from 5.7.3 to 5.7.4 in /nacossync-console/src/main/resources/static/console-fe
2020-03-15 18:14:41 +08:00
dependabot[bot] 9996a93ce7
chore(deps): bump acorn
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-15 10:11:57 +00:00
pader 4a6df3be28
Merge pull request #132 from nacos-group/develop
Deal with metadata format problems caused by Dubbo version #102
2020-01-11 02:33:01 +08:00
pader f97e6a129c
Merge pull request #131 from paderlol/develop
Deal with metadata format problems caused by Dubbo version #102
2020-01-11 02:31:52 +08:00
zhanglong 543a7c9509 Deal with metadata format problems caused by Dubbo version #102 2020-01-11 02:29:24 +08:00
pader 1f804a8587
Merge pull request #34 from nacos-group/develop
pull latest code from nacos sync
2020-01-11 02:22:21 +08:00
pader d6f98bbec2
Merge pull request #130 from MI-cool/ConsulUtils.transferMetadata-NPE
ConsulUtils.transferMetadata NPE
2020-01-07 16:21:44 +08:00
wanglinlin023 0110ed725e ConsulUtils.transferMetadata NPE 2020-01-07 15:38:34 +08:00
pader c2a60a8b2b
Merge pull request #128 from nacos-group/develop
Develop
2019-12-27 19:44:03 +08:00
pader 40c7e098f5
Merge pull request #127 from paderlol/develop
Develop
2019-12-27 19:25:07 +08:00
pader c3f01fb4f8
Merge pull request #33 from nacos-group/develop
Merge pull request #125 from paderlol/develop
2019-12-27 19:08:14 +08:00
zhanglong c1bb1f6590 Upgrade Nacos Sync 2019-12-27 18:39:18 +08:00
pader da636462f7
Merge pull request #125 from paderlol/develop
Fix Nacos instance don't remove when Eureka sync to Nacos #124
2019-12-27 18:24:45 +08:00
zhanglong 4f15a6217a Fix Nacos instance don't remove when Eureka sync to Nacos #124 2019-12-27 18:18:18 +08:00
pader f635e0ef1e
Merge pull request #32 from nacos-group/develop
Merge pull request #114 from paderlol/develop
2019-12-27 18:15:15 +08:00
pader 2c08639df9
Merge pull request #31 from nacos-group/develop
pull latest code
2019-12-27 18:12:25 +08:00
pader 9a79110b8e
Merge pull request #115 from nacos-group/develop
Develop
2019-12-12 21:33:17 +08:00
pader ad5e10fccb
Merge pull request #114 from paderlol/develop
Upgrade the client of nacos
2019-12-12 21:32:14 +08:00
zhanglong 81ff12b615 Upgrade the client of nacos 2019-12-12 21:29:35 +08:00
paderlol 899a8f452a Add migration type of support on readme 2019-11-06 13:48:48 -06:00
pader e1ea0a409c
Merge pull request #30 from nacos-group/develop
Pull the latest code from nacos group
2019-11-06 13:16:06 -06:00
pader 20c551954e
Merge pull request #109 from nacos-group/develop
Merge develop into master
2019-10-09 18:11:46 +08:00
pader 69a175d712
Merge pull request #108 from paderlol/develop
Upgrade Nacos sync version
2019-10-09 17:53:01 +08:00
pader e7a90a1be7
Merge pull request #29 from nacos-group/develop
Merge pull request #107 from paderlol/develop
2019-10-09 17:50:00 +08:00
pader.zhang 357fa3f205 Upgrade Nacos sync version 2019-10-09 17:48:23 +08:00
pader d8c66afab4
Merge pull request #107 from paderlol/develop
Remove the weight attribute when Nacos synchronizes Zookeeper
2019-10-09 17:23:29 +08:00
pader.zhang fc660bf219 Upgrade Nacos client version 2019-10-09 17:21:47 +08:00
pader d501042f6e
Merge pull request #106 from paderlol/master
Remove the weight attribute when Nacos synchronizes Zookeeper #105
2019-10-09 10:28:40 +08:00
pader.zhang a006d5abe5 Remove the weight attribute when Nacos synchronizes Zookeeper #105 2019-10-09 10:24:29 +08:00
pader 5cb1128216
Merge pull request #28 from nacos-group/develop
pull latest code
2019-10-08 11:52:58 +08:00
pader 89a8d5c6eb
Merge pull request #93 from JunRzz/feaure/nacos-to-eureka-update
nacos同步eureka时,增加维持心跳的线程池;eureka instance增加HealthCheck等url;
2019-10-08 11:50:45 +08:00
yongchao9 ef12b9bfac 关闭hibernate SQL输出 2019-06-30 09:19:07 +08:00
yongchao9 cb7071f25a 增加批量删除接口 deleteInBdeleteInBatch #89 2019-06-29 18:39:01 +08:00
yongchao9 853767e3ee 增加批量删除接口deleteInBatch 2019-06-29 18:27:54 +08:00
LiuJunJie c4305c11df nacos同步eureka时,增加维持心跳的线程池;eureka instance增加HealthCheck等url; 2019-06-26 11:11:38 +08:00
pader 7aa51bd4fb
Merge pull request #27 from nacos-group/develop
Merge pull request #86 from paderlol/develop
2019-06-09 21:05:01 +08:00
zhanglong 0bd70a4cf1 Fix license missing 2019-05-10 20:30:20 +08:00
Yann 2b0b1af2e8
Merge pull request #86 from paderlol/develop
Nacos sync to Consul #85
2019-05-10 17:46:05 +08:00
zhanglong 410bed774d Nacos sync to Eureka #87 2019-05-02 19:14:55 +08:00
zhanglong 489e2185d8 Nacos sync to Consul #85 2019-05-02 13:50:22 +08:00
pader 95919f93b2
Merge pull request #26 from nacos-group/master
pull latest code
2019-05-02 13:46:14 +08:00
Yann 479b2d07f2
Merge pull request #81 from nacos-group/develop
Develop
2019-04-22 23:18:08 +08:00
pader 4f5894a0b1
Merge pull request #25 from nacos-group/develop
pull latest code
2019-04-22 23:07:29 +08:00
wyp12 60d57e89e2 chore:解决空文件夹问题 2019-04-22 23:05:55 +08:00
pader 861ce04ddd
Merge pull request #24 from nacos-group/develop
pull latest code
2019-04-22 22:53:49 +08:00
wyp12 0acb811025 chore:增加logs创建命令 2019-04-22 22:41:38 +08:00
pader 0210fe078d
Merge pull request #23 from nacos-group/develop
pull latest code
2019-04-22 22:19:21 +08:00
wyp12 f28cd0b60c chore:修复启动脚本 2019-04-22 20:17:04 +08:00
Yann 7c6f6e1bd4
Merge pull request #80 from nacos-group/develop
Develop
2019-04-22 18:41:34 +08:00
wyp12 bd4ece9fcb chore:更新ReadMe 2019-04-22 18:40:02 +08:00
wyp12 71d69822ab fix:启动脚本不与版本号耦合 2019-04-22 18:36:40 +08:00
pader.zhang 54edcf1e7d Batch adding sync tasks for Nacos 2019-04-19 09:42:13 +08:00
pader 79dd85b76d
Merge pull request #22 from nacos-group/develop
pull the latest code
2019-03-31 13:00:40 +08:00
pader 85063676de
Merge pull request #21 from nacos-group/master
pull the latest code
2019-03-31 12:53:33 +08:00
wyp12 29fc69ff9e feat:add more metrics 2019-02-28 18:29:46 +08:00
wyp12 d62da32b02 docs:merge devlop 2019-02-28 17:11:09 +08:00
wyp12 ed9f62b0de docs:upate version 2019-02-28 16:58:28 +08:00
Yann ecd9d4c064
Update README.md 2019-02-28 16:21:13 +08:00
wyp12 cf79ef16b3 fix:#72 2019-02-28 16:05:14 +08:00
wyp12 0f6ec88966 Merge branch 'develop' of github.com:nacos-group/nacos-sync into develop 2019-02-28 15:56:56 +08:00
wyp12 c5a411787b feat:add metrics 2019-02-28 15:56:44 +08:00
Yann 94324b88fb
Merge pull request #74 from mingyixu/master
add testcases for  api
2019-02-28 12:59:33 +08:00
Yann 10677202c5
Merge pull request #73 from nacos-group/revert-70-master
Revert "新增接口用例"
2019-02-28 12:03:04 +08:00
Yann c309b91d63
Revert "新增接口用例" 2019-02-28 11:59:21 +08:00
Yann 651454cf89
Merge pull request #70 from mingyixu/master
新增接口用例
2019-02-28 11:58:27 +08:00
xiaochun.xxc cfa885138b add testcases” 2019-02-28 11:32:14 +08:00
xiaochun.xxc 17cd7161c8 新增接口用例 2019-02-27 10:56:31 +08:00
Yann 3064410f28
Merge pull request #67 from paderlol/develop
Fix #66
2019-02-01 16:10:27 +08:00
pader.zhang 7ffefc0bb1 Fix #66 2019-01-29 15:37:27 +08:00
pader a00dc097e5
Merge pull request #20 from nacos-group/develop
Merge pull request #65 from paderlol/develop
2019-01-29 10:54:48 +08:00
Yann 961b5f3ad6
Merge pull request #65 from paderlol/develop
when deleting zk sync to Nacos, throw the NPE #64
2019-01-28 18:02:12 +08:00
pader.zhang 48e7e8f2d3 Fix when deleting zk sync to Nacos, throw the NPE #64 2019-01-28 17:48:47 +08:00
pader 35e7164eb9
Merge pull request #19 from nacos-group/develop
Merge pull request #62 from paderlol/develop
2019-01-28 17:44:08 +08:00
pader c732c4a790
Merge pull request #62 from paderlol/develop
Optimized Nacos sync to Zookeeper #61
2019-01-26 17:16:27 +08:00
zhanglong 2e6c2cce01 Optimized Nacos sync to Zookeeper #61 2019-01-26 17:15:21 +08:00
pader 7237ef9465
Merge pull request #18 from nacos-group/develop
merge latest code
2019-01-26 15:02:16 +08:00
wyp12 847e4938dc fix:update the version 2019-01-21 14:47:21 +08:00
wyp12 ddd6aa8748 Merge branch 'develop' of github.com:nacos-group/nacos-sync into develop 2019-01-21 14:42:10 +08:00
wyp12 7e94c13918 fix:do not kill process before start 2019-01-21 14:41:52 +08:00
pader 69b48df8c5
Merge pull request #17 from nacos-group/develop
Merge pull request #56 from paderlol/develop
2019-01-17 11:56:03 +08:00
pader 8d2fecf793
Merge pull request #56 from paderlol/develop
Optimize getting cluster connections #55
2019-01-17 11:54:53 +08:00
pader.zhang 50c731f6ff Optimize getting cluster connections #55 2019-01-17 11:52:57 +08:00
pader 0b4fe40d4d
Merge pull request #16 from nacos-group/develop
Merge latest code
2019-01-17 10:12:54 +08:00
Yann a09c564a8f
Merge pull request #54 from paderlol/develop
Refactor zookeeper create client instance #53
2019-01-16 22:35:56 +08:00
zhanglong 1a8bdf9850 Optimize the exception handling #53 2019-01-16 21:48:14 +08:00
pader.zhang 72ff493469 Refactor zookeeper create client instance #53 2019-01-16 18:03:29 +08:00
pader 1937316b00
Merge pull request #15 from nacos-group/develop
merge latest code
2019-01-16 16:52:16 +08:00
wyp12 bc734032aa fix:#44 2019-01-16 16:01:22 +08:00
LoadChange b0fa9372db Merge branch 'develop' of github.com:nacos-group/nacos-sync into develop 2019-01-16 15:57:14 +08:00
LoadChange d85d247452 Update fe build 2019-01-16 15:56:30 +08:00
pader 8b2077da97
Merge pull request #14 from nacos-group/develop
merge latest code
2019-01-16 09:49:05 +08:00
wyp12 a720379f0c fix: #48 2019-01-16 00:28:45 +08:00
pader e4cef17fa7
Merge pull request #52 from paderlol/develop
Banner Dynamically get the version number #51
2019-01-15 22:56:13 +08:00
zhanglong a11940ad67 Banner Dynamically get the version number #51 2019-01-15 22:55:04 +08:00
pader 6413d19ee8
Merge pull request #13 from nacos-group/develop
fix:update file
2019-01-15 22:54:17 +08:00
wyp12 f58d0104e5 fix:update file 2019-01-15 22:52:02 +08:00
pader df18e6665f
Merge pull request #12 from nacos-group/develop
merge latest code
2019-01-15 22:43:49 +08:00
wyp12 3a421f565f fix:update log path 2019-01-15 22:42:07 +08:00
pader ea920079a7
Merge pull request #11 from nacos-group/develop
merge latest code
2019-01-15 21:33:17 +08:00
wyp12 f445eec060 chore:#48,#39 add shell&log file 2019-01-15 20:55:34 +08:00
Yann 3ab307d000
Merge pull request #50 from paderlol/develop
Code comment Chinese unified into English
2019-01-15 17:03:17 +08:00
pader.zhang f3c0ac3965 Code comment Chinese unified into English #46 2019-01-15 16:55:08 +08:00
mingyixu 53f385d62e
Merge pull request #49 from mingyixu/master
add testcases
2019-01-15 16:54:47 +08:00
mingyixu 66ee96c060
Merge branch 'develop' into master 2019-01-15 16:54:34 +08:00
xiaochun.xxc ee7c8dcb90 add testcases 2019-01-15 16:47:44 +08:00
pader b342e32d95
Merge pull request #10 from nacos-group/develop
merge latest code
2019-01-15 16:34:16 +08:00
wyp12 fb97bae4c3 Merge branch 'develop' of github.com:nacos-group/nacos-sync into develop 2019-01-15 02:33:39 +08:00
wyp12 727d062bb7 chore:resolve #39 2019-01-15 02:33:22 +08:00
LoadChange 7413cf2f28 fix: Fixes #44, Feature #41 2019-01-14 17:08:06 +08:00
wyp12 ee4ec0e4c8 fix:#44 Http DELETE 参数传递统一改成非BODY 2019-01-14 11:12:36 +08:00
wyp12 3012a02323 feat:不支持的同步类型提示给到用户 2019-01-13 23:10:47 +08:00
pader d7b540897e
Merge pull request #9 from nacos-group/develop
Merge pull request #43 from paderlol/develop
2019-01-13 22:57:35 +08:00
pader ec8948748f
Merge pull request #43 from paderlol/develop
Add an customize banner #42
2019-01-13 22:56:17 +08:00
zhanglong 3569481266 Add an customize banner #42 2019-01-13 22:55:01 +08:00
pader 8db3e8c243
Merge pull request #8 from nacos-group/develop
pull code
2019-01-13 22:51:45 +08:00
Yann cd2b981a7e
Update README.md
add Web Console Url
2019-01-13 18:01:05 +08:00
wyp12 3defb3490a fix:隐藏系统配置项功能 2019-01-13 17:33:27 +08:00
wyp12 4ffc14ab63 docs:增加开源协议说明 2019-01-13 17:02:49 +08:00
pader dac985047f
Merge pull request #37 from paderlol/develop
Catch exceptions for StringUtils Class and print warning logs #36
2019-01-13 14:58:38 +08:00
zhanglong 49fba56703 Catch exceptions for StringUtils Class and print warning logs #36 2019-01-13 14:57:46 +08:00
pader f90b4a1c31
Merge pull request #7 from nacos-group/develop
Merge pull request #35 from paderlol/develop
2019-01-13 00:31:42 +08:00
pader ecc74f903e
Merge pull request #35 from paderlol/develop
Add NacosSync Test Case #28
2019-01-13 00:30:41 +08:00
zhanglong 9f3e9355dc Add NacosSync Test Case #28 2019-01-13 00:28:33 +08:00
pader 3272914f72
Merge pull request #6 from nacos-group/develop
Merge pull request #34 from paderlol/develop
2019-01-13 00:27:20 +08:00
pader c58ec08d2a
Merge pull request #34 from paderlol/develop
Eureka and Consul can only be valid once when syncing to Nacos #33
2019-01-13 00:26:27 +08:00
zhanglong 55a42d4954 Fix Eureka and Consul can only be valid once when syncing to Nacos #33 2019-01-13 00:25:33 +08:00
pader 801436774b
Merge pull request #5 from nacos-group/develop
Merge pull request #32 from nacos-group/develop
2019-01-12 20:12:50 +08:00
pader 769a181e43 Merge pull request #32 from paderlol/develop
Parsing the dubbo protocol name is incorrect #31
2019-01-12 20:09:40 +08:00
zhanglong 48c20dd421 Fix parsing the dubbo protocol name is incorrect #31 2019-01-12 20:08:56 +08:00
pader a8c7211748
Merge pull request #4 from nacos-group/develop
Merge pull request #30 from paderlol/develop
2019-01-12 17:04:54 +08:00
pader 9896807332
Merge pull request #30 from paderlol/develop
Weight field type in Dubbo and Nacos are different
2019-01-12 17:03:27 +08:00
zhanglong b4560e5ddc fix Nacos weight field rounded up #29 2019-01-12 17:01:44 +08:00
pader 693fe3bc34
Merge pull request #3 from nacos-group/develop
Merge pull request
2019-01-12 12:17:22 +08:00
王彦民 91bc50cdc8 fix: hidden SystemConfig menu closes #26 2019-01-11 13:05:52 +08:00
wyp12 c5c9ac0659 Merge remote-tracking branch 'origin/master' into develop 2019-01-11 10:15:38 +08:00
pader d91e7e41e7
Merge pull request #2 from nacos-group/develop
update
2019-01-06 21:58:24 +08:00
pader e6980cec19
Merge pull request #22 from paderlol/develop
Nacos Sync to Dubbo Zookeeper registry #21
2019-01-06 21:45:38 +08:00
zhanglong 7ade3c5e3b Nacos Sync to Dubbo Zookeeper registry #21 2019-01-06 21:42:37 +08:00
pader 85d48c20ca
Merge pull request #1 from nacos-group/develop
Merge pull request #19 from paderlol/develop
2019-01-06 15:05:22 +08:00
pader 954846a1e8
Merge pull request #19 from paderlol/develop
Supports two-way synchronization registration services #18
2019-01-06 14:57:29 +08:00
zhanglong 6e34dfb851 Refactor SyncService #20 2019-01-06 14:49:13 +08:00
zhanglong 94bc8727ae Supports two-way synchronization registration services 2019-01-06 14:36:25 +08:00
zhanglong 57e60d2fc9 Add Consul synchronization to Nacos function for issue #17 2018-12-31 23:29:34 +08:00
zhanglong f7320d29f3 Add Eureka synchronization to NACOS function for issue #16 2018-12-31 20:08:35 +08:00
zhanglong 9dbb59d86d Add ZK synchronization to NACOS function #13 2018-12-26 23:06:47 +08:00
zhanglong 800cc493ad Add ZK synchronization to NACOS function 2018-12-25 23:55:40 +08:00
王彦民 216b0ec6fa fix: i18n handover bug 2018-12-23 17:48:55 +08:00
王彦民 31cb8c8a01 refactor: Console FE project, #15 2018-12-23 17:07:32 +08:00
wyp12 09ecbe2cd2 fix:更新前端页面 2018-12-21 22:37:13 +08:00
王彦民 ad33285e04 fix: 源集群、目标集群字段显示 2018-12-21 01:51:28 +08:00
王彦民 8db18d1104 fix: i18n && fe build 2018-12-21 00:51:23 +08:00
王彦民 f8375f9f55 Merge branch 'develop' of github.com:nacos-group/nacos-sync into develop 2018-12-21 00:32:18 +08:00
王彦民 0618fb6879 fix: 对接AddSyncDialog 接口 2018-12-21 00:31:32 +08:00
wyp12 f223d3d8cd fix:change path 2018-12-19 22:06:41 +08:00
王彦民 2e0448fc62 fix: Console Task Manager Optimize #12 2018-12-19 21:34:24 +08:00
wyp12 c670da7d46 fix:删除无效 2018-12-11 14:34:52 +08:00
wyp12 874c7b4c1f fix:删除冲突文件 2018-12-11 14:33:29 +08:00
wyp12 6fff8e514a fix:更新前端文件 2018-12-11 14:28:00 +08:00
wyp12 7af8b35424 fix:修复页面 2018-12-11 14:27:11 +08:00
王彦民 c185d10a26 fix: Internationalized display 2018-12-11 14:25:32 +08:00
王彦民 7a703352c5 Update: fe release 2018-12-11 11:09:22 +08:00
Yann dc45639f68
Merge pull request #11 from nanamikon/master
sync nacos to nacos
2018-12-10 10:37:03 +08:00
kaitai.zj 00c93b7234 不需要重复对task id做唯一健 2018-12-10 09:47:20 +08:00
kaitai.zj 9750f988e9 全部异常捕获, 避免漏掉一些日志 2018-12-05 15:17:33 +08:00
kaitai.zj 25919e0dfa 添加空判断 2018-12-05 15:14:27 +08:00
kaitai.zj f2f3dbc5a6 sync nacos to nacos 2018-12-04 21:18:49 +08:00
188 changed files with 12146 additions and 3902 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ target
node_modules
test/derby.log
/console-fe
.flattened-pom.xml

130
README.md
View File

@ -1,9 +1,11 @@
# NacosSync
# Nacos Sync
## [Example](https://github.com/paderlol/nacos-sync-example)
## Function
- Console: provide API and console for management
- Worker: provider the service registration synchronization.
- Worker: provide the service registration synchronization.
## Architecture
@ -43,6 +45,128 @@ Info | +------------+ ^
## Quick Start:
- Swagger API: http://127.0.0.1:8081/swagger-ui.html#/
- Swagger API: http://127.0.0.1:8083/swagger-ui.html#/
- Web Console: http://127.0.0.1:8083/
- Others: TBD
# NacosSync Migration User Guide
## Support migration type
| Source | Target | Support | Note |
| --------- | --------- | ------- | ------------------------------------------------------------ |
| Nacos | Nacos | Yes | Only supports the same version of Nacos migration,**especially** the version of **0.8** migrates to 1.0 or above. |
| Nacos | Zookeeper | Yes | Only support registery center of **Dubbo** |
| Nacos | Consul | Yes | Only support registery center of **Spring Cloud** |
| Nacos | Eureka | Yes | Only support registery center of **Spring Cloud** |
| Zookeeper | Nacos | Yes | Only support registery center of **Dubbo** |
| Consul | Nacos | Yes | Only support registery center of **Spring Cloud** |
| Eureka | Nacos | Yes | Only support registery center of **Spring Cloud** |
## Manual Goal
- Start the NacosSync service
- Use a simple example to demonstrate how to migrate a Dubbo client registered in the Zookeeper Registry to the Nacos Registry
## Prerequisites
Before you begin, install the following:
- 64bit OS: Linux/Unix/Mac/Windows supported, Linux/Unix/Mac recommended.
- 64bit JDK 1.8+: downloads, JAVA_HOME settings.
- Maven 3.5.2+: [downloads](https://maven.apache.org/download.cgi), [settings](https://maven.apache.org/settings.html).
- MySql 5.6.+
## Download & Build From Release
There are two ways to get NacosSync.
- Download run package
- Download source code from Github
``` xml
cd nacos-sync/
mvn clean package -U
```
The path to the target file:
``` xml
nacos-sync/nacossync-distribution/target/nacos-sync-0.5.0.tar.gz
```
After extracting the installation package, the directory structure:
``` xml
nacos-sync
├── LICENSE
├── NOTICE
├── bin
│   ├── nacosSync.sql
│   ├── shutdown.sh
│   └── startup.sh
├── conf
│   ├── application.properties
│   └── logback-spring.xml
├── logs
└── nacos-sync-server.jar
```
## Initialize The DB
The default is Mysql database, which can support other relational databases
- Build db schema, the default schema name nacos_sync.
- Tables do not need to be created separately, which is conducive to hibernate's automatic table creation function.
- If the automatic table creation fails, you can build the table nacosSync.sql, the table statement is in the bin folder.
## DB Configuration
In the bin folder, application.properties:
``` xml
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/nacos_sync?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
```
## Start Server
``` xml
$ nacosSync/bin:
sh startup.sh start
```
## Admin Console
``` xml
http://127.0.0.1:8083/#/serviceSync
```
## 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)

BIN
img_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
img_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>nacossync-parent</artifactId>
<groupId>com.alibaba.nacossync</groupId>
<version>0.1.0</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -27,5 +27,4 @@
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@ -1,11 +1,20 @@
{
"presets": [
"env",
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
],
"react-app"
],
"plugins": [
"transform-react-es6-displayname",
"transform-decorators-legacy",
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"babel-plugin-import",
{

View File

@ -0,0 +1,15 @@
{
"extends": "eslint-config-ali/react",
"parser": "babel-eslint",
"env": {
"mocha": true
},
"globals": {
"GLOBAL": true
},
"rules": {
"max-len": 0,
"react/prop-types": 0,
"react/jsx-no-target-blank": 0
}
}

View File

@ -20,6 +20,11 @@ module.exports = {
"css-loader",
"sass-loader"
]
}, {
enforce: 'pre',
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'eslint-loader',
}, {
test: /\.(js|jsx)$/,
exclude: /node_modules/,

View File

@ -7,7 +7,7 @@ module.exports = Object.assign({}, base, {
context: ['/nacossync'],
changeOrigin: true,
secure: false,
target: 'http://10.101.15.150:8081'
target: 'http://30.5.121.153:8081'
}],
disableHostCheck: true
},

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Nacos-Sync</title>
<link rel="shortcut icon" href="//www.aliyun.com/favicon.ico" type="image/x-icon">
<link href="./css/main.e2917886.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="./js/main.b73436b5.js"></script></body>
</html>

View File

@ -4,6 +4,9 @@
"description": "console fe",
"main": "index.js",
"scripts": {
"eslint": "eslint src/",
"eslint-output": "eslint -f html src/ > eslint-result.html",
"eslint-fix": "eslint --fix src/",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.conf.js --open",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js && node build/copy-dist.js"
},
@ -14,39 +17,43 @@
"url": "git+https://github.com/nacos-group/nacos-sync.git"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-import": "^1.10.0",
"babel-plugin-transform-decorators": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-plugin-transform-react-es6-displayname": "^1.0.0-beta1.4",
"babel-preset-env": "^1.7.0",
"babel-preset-react-app": "^3.1.1",
"babel-runtime": "^6.23.0",
"clean-webpack-plugin": "^0.1.19",
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-decorators": "^7.2.3",
"@babel/preset-env": "^7.2.3",
"@babel/runtime": "^7.2.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"babel-plugin-import": "^1.11.0",
"babel-preset-react-app": "^6.1.0",
"clean-webpack-plugin": "^1.0.0",
"cross-env": "^5.2.0",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"css-loader": "^2.0.2",
"eslint": "^5.11.0",
"eslint-config-ali": "^4.1.0",
"eslint-loader": "^2.1.1",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.4.3",
"node-sass": "^4.9.3",
"mini-css-extract-plugin": "^0.5.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.0",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "^1.1.1",
"webpack": "^4.20.2",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.1.0",
"url-loader": "^1.1.2",
"webpack": "^4.28.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9"
"webpack-dev-server": "^3.1.13"
},
"dependencies": {
"@alifd/next": "^1.7.6",
"@alifd/next": "^1.11.5",
"axios": "^0.18.0",
"moment": "^2.22.2",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react-redux": "^5.1.0",
"moment": "^2.23.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^5.1.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-router-redux": "^4.0.8",

View File

@ -1,18 +1,16 @@
import React from 'react'
import './index.scss'
import React from 'react';
import './index.scss';
class FuncHead extends React.Component {
render() {
const {title, subtitle} = this.props
return (
<div className="function-header">
<h4 className="main-title">{title}</h4>
{
subtitle ? <span className="subtitle">{subtitle}</span> : null
}
</div>
)
}
render() {
const { title, subtitle } = this.props;
return (
<div className="function-header">
<h4 className="main-title">{title}</h4>
{subtitle && <span className="subtitle">{subtitle}</span>}
</div>
);
}
}
export default FuncHead
export default FuncHead;

View File

@ -1,3 +1,3 @@
import FuncHead from './FuncHead'
import FuncHead from './FuncHead';
export default FuncHead
export default FuncHead;

View File

@ -1,65 +1,62 @@
import React from 'react'
import {connect} from "react-redux"
import {ConfigProvider} from '@alifd/next'
import LogoImage from './images/logo.svg'
import {changeLanguage} from '../../reducers/locale'
import React from 'react';
import { connect } from 'react-redux';
import { ConfigProvider } from '@alifd/next';
import LogoImage from './images/logo.svg';
import { changeLanguage } from '../../reducers/locale';
import './index.scss'
import './index.scss';
@connect(state => ({...state.locale}), {changeLanguage})
@connect(state => ({ ...state.locale }), { changeLanguage })
@ConfigProvider.config
class Header extends React.Component {
constructor(props) {
super(props)
}
static displayName = 'Header'
languageSwitching() {
const {language = 'en-US', changeLanguage} = this.props
changeLanguage(language === 'en-US' ? 'zh-CN' : 'en-US')
}
languageSwitching() {
const { language = 'en-US' } = this.props;
this.props.changeLanguage(language === 'en-US' ? 'zh-CN' : 'en-US');
}
render() {
const {locale = {}, language = 'en-US'} = this.props
const {home, docs, blog, community, languageSwitchButton} = locale
const BASE_URL = `https://nacos.io/${language.toLocaleLowerCase()}/`
const NAV_MENU = [{
id: 1,
title: home,
link: BASE_URL
}, {
id: 2,
title: docs,
link: `${BASE_URL}docs/what-is-nacos.html`
}, {
id: 3,
title: blog,
link: `${BASE_URL}blog/index.html`
}, {
id: 4,
title: community,
link: `${BASE_URL}community/index.html`
}]
return (
<header className="header-container-primary">
<a href="/" className="logo" title="Nacos-Sync">
<img src={LogoImage} className="logo-img"/>
</a>
<span
className="language-switch language-switch-primary"
onClick={() => this.languageSwitching()}
>{languageSwitchButton}</span>
<ul className="nav-menu">
{
NAV_MENU.map(item => (
<li key={item.id}>
<a href={item.link} target="_blank">{item.title}</a>
</li>
))
}
</ul>
</header>
)
}
render() {
const { locale = {}, language = 'en-US' } = this.props;
const { home, docs, blog, community, languageSwitchButton } = locale;
const BASE_URL = `https://nacos.io/${language.toLocaleLowerCase()}/`;
const NAV_MENU = [{
id: 1,
title: home,
link: BASE_URL,
}, {
id: 2,
title: docs,
link: `${BASE_URL}docs/what-is-nacos.html`,
}, {
id: 3,
title: blog,
link: `${BASE_URL}blog/index.html`,
}, {
id: 4,
title: community,
link: `${BASE_URL}community/index.html`,
}];
return (
<header className="header-container-primary">
<a href="/" className="logo" title="Nacos-Sync">
<img src={LogoImage} className="logo-img" />
</a>
<span className="language-switch language-switch-primary" onClick={() => this.languageSwitching()}>
{languageSwitchButton}
</span>
<ul className="nav-menu">
{
NAV_MENU.map(item => (
<li key={item.id}>
<a href={item.link} target="_blank">{item.title}</a>
</li>
))
}
</ul>
</header>
);
}
}
export default Header
export default Header;

View File

@ -1,3 +1,3 @@
import Header from './Header'
import Header from './Header';
export default Header
export default Header;

View File

@ -1,67 +1,68 @@
import React from 'react'
import {withRouter} from 'react-router-dom'
import {ConfigProvider, Nav} from '@alifd/next'
import './index.scss'
import React from 'react';
import { withRouter } from 'react-router-dom';
import { ConfigProvider, Nav } from '@alifd/next';
import './index.scss';
const {Item, SubNav} = Nav
const { Item, SubNav } = Nav;
@withRouter
@ConfigProvider.config
class Menu extends React.Component {
static displayName = 'Menu'
static displayName = 'Menu'
constructor(props) {
super(props)
}
_getSubNav(menuItem) {
return (
<SubNav label={menuItem.title} key={menuItem.id}>
{menuItem.child.map(item => (item.child ? this._getSubNav(item) : this._getItem(item)))}
</SubNav>
);
}
_getSubNav(menuItem) {
return (
<SubNav label={menuItem.title} key={menuItem.id}>
{menuItem.child.map(item => item.child ? this._getSubNav(item) : this._getItem(item))}
</SubNav>
)
}
_getItem(menuItem) {
return (
<Item
key={menuItem.id}
onSelect={() => {
if (this.props.location.pathname === menuItem.link) return;
this.props.history.push(menuItem.link);
}}
>
{menuItem.title}
</Item>
);
}
_getItem(menuItem) {
return <Item
key={menuItem.id}
onSelect={() => {
if (this.props.location.pathname === menuItem.link) return
this.props.history.push(menuItem.link)
}}
>{menuItem.title}</Item>
}
generateMenu() {
return this.buildMenuData().map(item => (item.child ? this._getSubNav(item) : this._getItem(item)));
}
generateMenu() {
return this.buildMenuData().map(item => (item.child ? this._getSubNav(item) : this._getItem(item)))
}
buildMenuData() {
const { locale = {} } = this.props;
// const { serviceSync, clusterConfig, systemConfig } = locale;
const { serviceSync, clusterConfig } = locale;
return [{
id: 1,
title: serviceSync,
link: '/serviceSync',
}, {
id: 2,
title: clusterConfig,
link: '/clusterConfig',
}];
// , {
// id: 3,
// title: systemConfig,
// link: '/systemConfig',
// }];
}
buildMenuData() {
const {locale = {}} = this.props
const {serviceSync, clusterConfig, systemConfig} = locale
return [{
id: 1,
title: serviceSync,
link: '/serviceSync'
}, {
id: 2,
title: clusterConfig,
link: '/clusterConfig'
}, {
id: 3,
title: systemConfig,
link: '/systemConfig'
}]
}
render() {
return (
<div className="menu">
<Nav openMode="multiple">{this.generateMenu()}</Nav>
</div>
)
}
render() {
return (
<div className="menu">
<Nav openMode="multiple">{this.generateMenu()}</Nav>
</div>
);
}
}
export default Menu
export default Menu;

View File

@ -1,3 +1,3 @@
import Menu from './Menu'
import Menu from './Menu';
export default Menu
export default Menu;

View File

@ -1,13 +1,15 @@
export const LANGUAGE_KEY = 'site_language'
export const LANGUAGE_SWITCH = 'LANGUAGE_SWITCH'
export const LANGUAGE_KEY = 'site_language';
export const LANGUAGE_SWITCH = 'LANGUAGE_SWITCH';
export const SUCCESS_RESULT_CODE = 'SUCCESS'
export const SUCCESS_RESULT_CODE = 'SUCCESS';
export const CLUSTER_LIST = 'CLUSTER_LIST'
export const GET_CLUSTER_BY_ID = 'GET_CLUSTER_BY_ID'
export const CLUSTER_TYPES = 'CLUSTER_TYPES'
export const CLUSTER_LIST = 'CLUSTER_LIST';
export const GET_CLUSTER_BY_ID = 'GET_CLUSTER_BY_ID';
export const CLUSTER_TYPES = 'CLUSTER_TYPES';
export const TASK_LIST = 'TASK_LIST'
export const GET_TASK_BY_ID = 'GET_TASK_BY_ID'
export const TASK_LIST = 'TASK_LIST';
export const GET_TASK_BY_ID = 'GET_TASK_BY_ID';
export const PAGE_SIZE = 10
export const PAGE_SIZE = 10;
export const REDUX_DEVTOOLS = '__REDUX_DEVTOOLS_EXTENSION__';

View File

@ -1,85 +1,123 @@
import React from 'react'
import {Form, Input, Select, Dialog, ConfigProvider} from '@alifd/next'
import {connect} from 'react-redux'
import {getTypes, add} from '../../reducers/cluster'
import '../../style/dialog-form.scss'
import React from 'react';
import { Form, Input, Select, Dialog, ConfigProvider } from '@alifd/next';
import { connect } from 'react-redux';
import { getTypes, add } from '../../reducers/cluster';
import '../../style/dialog-form.scss';
const FormItem = Form.Item
const {Option} = Select
const FormItem = Form.Item;
const { Option } = Select;
@connect(state => ({...state.cluster}), {getTypes}, null, {withRef: true})
@connect(state => ({ ...state.cluster }), { getTypes }, null, { withRef: true })
@ConfigProvider.config
class AddConfigDialog extends React.Component {
static displayName = 'AddConfigDialog'
constructor(props) {
super(props)
this.state = {
visible: false,
clusterName: '',
clusterType: '',
connectKeyList: []
}
super(props);
this.state = {
visible: false,
clusterName: '',
clusterType: '',
namespace: '',
password: '',
userName: '',
connectKeyList: [],
};
}
componentDidMount() {
this.props.getTypes()
this.props.getTypes();
}
save() {
const {clusterName, clusterType, connectKeyList} = this.state
add({clusterName, clusterType, connectKeyList})
.then(() => {
this.props.turnPage(1)
this.close()
})
.catch(() => this.close())
const { clusterName, namespace, userName, password, clusterType, connectKeyList } = this.state;
add({ clusterName, namespace, userName, password, clusterType, connectKeyList })
.then(() => {
this.props.turnPage(1);
this.close();
})
.catch(() => this.close());
}
close() {
this.setState({visible: false})
this.setState({
visible: false,
clusterType: '',
});
}
open = () => this.setState({visible: true})
open = () => this.setState({ visible: true })
render() {
const {types = [], locale = {}} = this.props
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}>
<Form>
<FormItem label={`${locale.clusterName}:`}>
<Input
onChange={clusterName => this.setState({clusterName})}
placeholder={locale.clusterNamePlaceholder}
/>
</FormItem>
<FormItem label={`${locale.clusterType}:`}>
<Select
onChange={clusterType => this.setState({clusterType})}
>
{
const { types = [], locale = {} } = this.props;
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}
>
<Form>
<FormItem label={`${locale.clusterName}:`}>
<Input
onChange={clusterName => this.setState({ clusterName })}
placeholder={locale.clusterNamePlaceholder}
/>
</FormItem>
<FormItem label={`${locale.clusterType}:`}>
<Select
onChange={clusterType => this.setState({ clusterType })}
>
{
types.map(type => (
<Option value={type} key={type}>{type}</Option>
<Option value={type} key={type}>{type}</Option>
))
}
</Select>
</FormItem>
<FormItem label={`${locale.connectKeyList}:`}>
<Input.TextArea
onChange={connectKeyListStr => {
this.setState({connectKeyList: connectKeyListStr.split('\n')})
}}
placeholder={locale.connectKeyListPlaceholder}
/>
</FormItem>
</Form>
</Dialog>
)
</Select>
</FormItem>
{
this.state.clusterType === 'NACOS' && (
<>
<FormItem
label={`${locale.namespace}:`}
>
<Input
placeholder={locale.namespacePlaceholder}
onChange={ns => this.setState({ namespace: ns })}
/>
</FormItem>
<FormItem
label={`${locale.username}:`}
>
<Input
placeholder={locale.usernamePlaceholder}
onChange={un => this.setState({ userName: un })}
/>
</FormItem>
<FormItem
label={`${locale.password}:`}
>
<Input.Password
placeholder={locale.passwordPlaceholder}
onChange={pw => this.setState({ password: pw })}
/>
</FormItem>
</>)
}
<FormItem label={`${locale.connectKeyList}:`}>
<Input.TextArea
onChange={(connectKeyListStr) => {
this.setState({ connectKeyList: connectKeyListStr.split('\n') });
}}
placeholder={locale.connectKeyListPlaceholder}
/>
</FormItem>
</Form>
</Dialog>
);
}
}
export default AddConfigDialog
export default AddConfigDialog;

View File

@ -1,124 +1,130 @@
import React from 'react'
import {connect} from 'react-redux'
import {Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider} from '@alifd/next'
import FuncHead from '../../components/FuncHead'
import AddConfigDialog from './AddConfigDialog'
import {deleteCluster, list} from '../../reducers/cluster'
import './index.scss'
import React from 'react';
import { connect } from 'react-redux';
import { Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider } from '@alifd/next';
import FuncHead from '../../components/FuncHead';
import AddConfigDialog from './AddConfigDialog';
import { deleteCluster, list } from '../../reducers/cluster';
import './index.scss';
const FormItem = Form.Item
const FormItem = Form.Item;
@connect(state => ({...state.cluster}), {list})
@connect(state => ({ ...state.cluster }), { list })
@ConfigProvider.config
class ClusterConfig extends React.Component {
constructor(props) {
super(props)
this.addDialog = React.createRef()
this.state = {
pageNum: 1,
loading: true,
search: {
clusterName: ''
}
static displayName = 'ClusterConfig'
constructor(props) {
super(props);
this.addDialog = React.createRef();
this.state = {
pageNum: 1,
loading: true,
search: {
clusterName: '',
},
};
}
componentDidMount() {
const { pageNum } = this.state;
this.props.list({ pageNum }).then(() => this.loadComplete());
}
loadComplete() {
this.setState({ loading: false });
}
deleteServiceSync(record) {
const { clusterId } = record;
const { locale = {}, clusterModels = [] } = this.props;
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => deleteCluster({ clusterId }).then(() => {
let pageNum = 1;
if (this.state.pageNum > 1) {
if (clusterModels.length === 1) {
pageNum = this.state.pageNum - 1;
}
}
}
this.turnPage(pageNum);
Message.success(locale.successMsg);
}),
});
}
componentDidMount() {
const {pageNum} = this.state
const {list} = this.props
list({pageNum}).then(() => this.loadComplete())
}
turnPage(pageNum) {
const { search } = this.state;
this.setState({
pageNum,
loading: true,
}, () => this.props.list({ pageNum, ...search }).then(() => this.loadComplete()));
}
loadComplete() {
this.setState({loading: false})
}
onChangeSearchForm(obj) {
const { search } = this.state;
this.setState({ search: Object.assign({}, search, obj) });
}
deleteServiceSync(record) {
const {clusterId} = record
const {locale = {}, clusterModels = []} = this.props
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => deleteCluster({clusterId}).then(() => {
let pageNum = 1
if (this.state.pageNum > 1) {
if (clusterModels.length === 1) {
pageNum = this.state.pageNum - 1
}
}
this.turnPage(pageNum)
Message.success(locale.successMsg)
})
})
}
openAddDialog() {
this.addDialog.current.getWrappedInstance().getInstance().open();
}
turnPage(pageNum) {
const {search} = this.state
const {list} = this.props
this.setState({pageNum, loading: true}, () => list({pageNum, ...search}).then(() => this.loadComplete()))
}
render() {
const { loading, pageNum, search } = this.state;
const { types = [], clusterModels = [], locale = {}, totalSize = 0, totalPage = 0 } = this.props;
onChangeSearchForm(obj) {
const {search} = this.state
this.setState({search: Object.assign({}, search, obj)})
}
openAddDialog() {
this.addDialog.current.getWrappedInstance().getInstance().open()
}
render() {
const {loading, pageNum, search} = this.state
const {types = [], clusterModels = [], locale = {}, totalSize = 0, totalPage = 0} = this.props
return (
<div className="cluster-config">
<FuncHead title={locale.title}/>
<Form inline className="search-form">
<FormItem label={`${locale.clusterName}:`}>
<Input
style={{width: 198}}
value={search.clusterName}
placeholder={locale.clusterNamePlaceholder}
onChange={clusterName => this.onChangeSearchForm({clusterName})}
/>
</FormItem>
<Button type="primary" onClick={() => this.turnPage(1)}>{locale.search}</Button>
<Button
type="normal"
className="add-btn"
onClick={() => this.openAddDialog()}
>{locale.addCluster}</Button>
</Form>
<Table dataSource={clusterModels} loading={loading}>
<Table.Column title={locale.clusterName} dataIndex="clusterName"/>
<Table.Column title={locale.clusterType} dataIndex="clusterType"/>
<Table.Column title={locale.connectKeyList} dataIndex="connectKeyList"/>
<Table.Column
title={locale.operation}
cell={(value, index, record) => (
<Button
text
type="primary"
onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}</Button>
)}
/>
</Table>
{
totalPage > 1
? <Pagination
onChange={pageNum => this.turnPage(pageNum)}
current={pageNum}
total={totalSize}
className="list-pagination"
/>
: null
}
<AddConfigDialog ref={this.addDialog} types={types} turnPage={pn => this.turnPage(pn)}/>
</div>
)
}
return (
<div className="cluster-config">
<FuncHead title={locale.title} />
<Form inline className="search-form">
<FormItem label={`${locale.clusterName}:`}>
<Input
style={{ width: 198 }}
value={search.clusterName}
placeholder={locale.clusterNamePlaceholder}
onChange={clusterName => this.onChangeSearchForm({ clusterName })}
/>
</FormItem>
<Button type="primary" onClick={() => this.turnPage(1)}>{locale.search}</Button>
<Button
type="normal"
className="add-btn"
onClick={() => this.openAddDialog()}
>{locale.addCluster}
</Button>
</Form>
<Table dataSource={clusterModels} loading={loading}>
<Table.Column title={locale.clusterName} dataIndex="clusterName" />
<Table.Column title={locale.clusterType} dataIndex="clusterType" />
<Table.Column title={locale.connectKeyList} dataIndex="connectKeyList" />
<Table.Column title={locale.namespace} dataIndex="namespace" />
<Table.Column
title={locale.operation}
cell={(value, index, record) => (
<Button
text
type="primary"
onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}
</Button>
)}
/>
</Table>
{
totalPage > 1 && (
<Pagination
onChange={currentPageNum => this.turnPage(currentPageNum)}
current={pageNum}
total={totalSize}
className="list-pagination"
/>
)
}
<AddConfigDialog ref={this.addDialog} types={types} turnPage={pn => this.turnPage(pn)} />
</div>
);
}
}
export default ClusterConfig
export default ClusterConfig;

View File

@ -1,3 +1,3 @@
import ClusterConfig from './ClusterConfig'
import ClusterConfig from './ClusterConfig';
export default ClusterConfig
export default ClusterConfig;

View File

@ -1,26 +1,26 @@
import React from 'react'
import {Grid} from '@alifd/next'
import Header from '../../components/Header'
import Menu from '../../components/Menu'
import './index.scss'
import React from 'react';
import { Grid } from '@alifd/next';
import Header from '../../components/Header';
import Menu from '../../components/Menu';
import './index.scss';
const {Row, Col} = Grid
const { Row, Col } = Grid;
class Layout extends React.Component {
render() {
return (
<div className="containers">
<Header/>
<Row className="layout">
<Col fixedSpan="9" className="nav-bar">
<h1 className="title">Nacos-Sync 3.0</h1>
<Menu/>
</Col>
<Col className="main-panel">{this.props.children}</Col>
</Row>
</div>
)
}
render() {
return (
<div className="containers">
<Header />
<Row className="layout">
<Col fixedSpan="9" className="nav-bar">
<h1 className="title">Nacos-Sync 0.4.8</h1>
<Menu />
</Col>
<Col className="main-panel">{this.props.children}</Col>
</Row>
</div>
);
}
}
export default Layout
export default Layout;

View File

@ -1,3 +1,3 @@
import Layout from './Layout'
import Layout from './Layout';
export default Layout
export default Layout;

View File

@ -1,60 +1,113 @@
import React from 'react'
import {Form, Input, Select, Dialog, ConfigProvider} from '@alifd/next'
import '../../style/dialog-form.scss'
import React from 'react';
import { Form, Input, Select, Dialog, ConfigProvider } from '@alifd/next';
import '../../style/dialog-form.scss';
import connect from 'react-redux/es/connect/connect';
import { add } from '../../reducers/task';
import { list } from '../../reducers/cluster';
const FormItem = Form.Item
const FormItem = Form.Item;
const { Option } = Select;
@connect(state => ({ ...state.cluster }), { list }, null, { withRef: true })
@ConfigProvider.config
class AddSyncDialog extends React.Component {
constructor(props) {
super(props)
this.state = {
visible: false
}
}
static displayName = 'AddSyncDialog'
save() {
this.close()
console.log('save...')
}
constructor(props) {
super(props);
this.state = {
visible: false,
destClusterId: '',
groupName: '',
serviceName: '',
sourceClusterId: '',
version: '',
sourceCluster: {},
};
}
close() {
this.setState({visible: false})
}
componentDidMount() {
this.props.list({ pageSize: 10000000, pageNum: 1 });
}
open = () => this.setState({visible: true})
save() {
const { destClusterId, groupName, serviceName, sourceClusterId, version } = this.state;
add({ destClusterId, groupName, serviceName, sourceClusterId, version })
.then(() => {
this.props.turnPage(1);
this.close();
})
.catch(() => this.close());
}
render() {
const {locale = {}} = this.props
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}>
<Form>
<FormItem label={`${locale.serviceName}:`}>
<Input placeholder={locale.serviceNamePlaceholder}/>
</FormItem>
<FormItem label={`${locale.groupName}:`}>
<Input placeholder={locale.groupNamePlaceholder}/>
</FormItem>
<FormItem label={`${locale.sourceCluster}:`}>
<Select>
<Select.Option value="option1">CS</Select.Option>
</Select>
</FormItem>
<FormItem label={`${locale.destCluster}:`}>
<Select>
<Select.Option value="option1">Nacos</Select.Option>
</Select>
</FormItem>
</Form>
</Dialog>
)
}
close() {
this.setState({ visible: false, sourceCluster: {} });
}
onSourceClusterChange(sourceClusterId) {
const [sourceCluster] = this.props.clusterModels.filter(({ clusterId }) => clusterId === sourceClusterId);
this.setState({ sourceClusterId, sourceCluster });
}
open = () => this.setState({ visible: true })
render() {
const { sourceCluster } = this.state;
const { locale = {}, clusterModels = [] } = this.props;
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}
>
<Form>
<FormItem label={`${locale.serviceName}:`}>
<Input
placeholder={locale.serviceNamePlaceholder}
onChange={serviceName => this.setState({ serviceName })}
/>
</FormItem>
<FormItem label={`${locale.groupName}:`}>
<Input
placeholder={locale.groupNamePlaceholder}
onChange={groupName => this.setState({ groupName })}
/>
</FormItem>
{
sourceCluster.clusterType === 'ZK' && (
<FormItem label={`${locale.version}:`}>
<Input
placeholder={locale.versionPlaceholder}
onChange={version => this.setState({ version })}
/>
</FormItem>
)
}
<FormItem label={`${locale.sourceCluster}:`}>
<Select onChange={value => this.onSourceClusterChange(value)}>
{
clusterModels.map(({ clusterId, clusterName }) => (
<Option key={clusterId} value={clusterId}>{clusterName}</Option>
))
}
</Select>
</FormItem>
<FormItem label={`${locale.destCluster}:`}>
<Select onChange={destClusterId => this.setState({ destClusterId })}>
{
clusterModels.map(({ clusterId, clusterName }) => (
<Option key={clusterId} value={clusterId}>{clusterName}</Option>
))
}
</Select>
</FormItem>
</Form>
</Dialog>
);
}
}
export default AddSyncDialog
export default AddSyncDialog;

View File

@ -1,140 +1,187 @@
import React from 'react'
import {connect} from 'react-redux'
import {Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider} from '@alifd/next'
import FuncHead from '../../components/FuncHead'
import AddSyncDialog from './AddSyncDialog'
import {list, update} from '../../reducers/task'
import './index.scss'
import React from 'react';
import { connect } from 'react-redux';
import { Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider } from '@alifd/next';
import FuncHead from '../../components/FuncHead';
import AddSyncDialog from './AddSyncDialog';
import { list, update, deleteRow } from '../../reducers/task';
import { list as getClusterList } from '../../reducers/cluster';
import './index.scss';
const FormItem = Form.Item
const FormItem = Form.Item;
@connect(state => ({...state.task}), {list})
@connect(state => ({ ...state.task, clusterModels: state.cluster.clusterModels }), { list, getClusterList })
@ConfigProvider.config
class ServiceSync extends React.Component {
constructor(props) {
super(props)
this.addDialog = React.createRef()
this.state = {
pageNum: 1,
loading: true,
search: {
serviceName: ''
}
static displayName = 'ServiceSync'
constructor(props) {
super(props);
this.addDialog = React.createRef();
this.state = {
pageNum: 1,
loading: true,
search: {
serviceName: '',
},
};
}
componentDidMount() {
const { pageNum } = this.state;
this.props.list({ pageNum }).then(() => this.loadComplete());
this.props.getClusterList({ pageSize: 10000000, pageNum: 1 });
}
loadComplete() {
this.setState({ loading: false });
}
turnPage(pageNum) {
const { search } = this.state;
this.setState({
pageNum,
loading: true,
}, () => this.props.list({ pageNum, ...search }).then(() => this.loadComplete()));
}
deleteServiceSync(record) {
const { taskId } = record;
const { locale = {} } = this.props;
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => deleteRow({ taskId }).then(() => {
this.turnPage(this.state.pageNum);
Message.success(locale.deleteSuccessMsg);
}),
});
}
suspendedServiceSync(record) {
const { taskId } = record;
const { locale = {} } = this.props;
Dialog.confirm({
title: locale.confirm,
content: locale.suspendedMsg,
onOk: () => update({ taskId, taskStatus: 'DELETE' }).then(() => {
this.turnPage(this.state.pageNum);
Message.success(locale.successMsg);
}),
});
}
resynchronize(record) {
const { taskId } = record;
const { locale = {} } = this.props;
update({ taskId, taskStatus: 'SYNC' }).then(() => {
this.turnPage(this.state.pageNum);
Message.success(locale.syncSuccessMsg);
});
}
onChangeSearchForm(obj) {
const { search } = this.state;
this.setState({ search: Object.assign({}, search, obj) });
}
openAddDialog() {
this.addDialog.current.getWrappedInstance().getInstance().open();
}
render() {
const { loading, pageNum, search } = this.state;
const { taskModels = [], locale = {}, totalSize = 0, totalPage = 0, clusterModels = [] } = this.props;
const clusterMap = {};
clusterModels.forEach(({ clusterId, clusterName }) => {
clusterMap[clusterId] = clusterName;
});
return (
<div className="service-sync">
<FuncHead title={locale.title} />
<Form inline className="search-form">
<FormItem label={`${locale.serviceName}:`}>
<Input
style={{ width: 198 }}
value={search.serviceName}
placeholder={locale.serviceNamePlaceholder}
onChange={serviceName => this.onChangeSearchForm({ serviceName })}
/>
</FormItem>
<Button type="primary" onClick={() => this.turnPage(1)}>{locale.search}</Button>
<Button
type="normal"
className="add-btn"
onClick={() => this.openAddDialog()}
>{locale.addSync}
</Button>
</Form>
<Table dataSource={taskModels} loading={loading}>
<Table.Column title={locale.serviceName} dataIndex="serviceName" />
<Table.Column title={locale.groupName} dataIndex="groupName" />
<Table.Column
title={locale.sourceCluster}
dataIndex="sourceClusterId"
cell={key => clusterMap[key]}
/>
<Table.Column
title={locale.destCluster}
dataIndex="destClusterId"
cell={key => clusterMap[key]}
/>
<Table.Column
title={locale.operation}
cell={(value, index, record) => {
const buttonList = [];
if (record.taskStatus === 'SYNC') {
buttonList.push(
<Button
key="suspendedBtn"
text
type="primary"
onClick={() => this.suspendedServiceSync(record)}
>{locale.suspendedBtn}
</Button>,
);
}
if (record.taskStatus === 'DELETE') {
buttonList.push(
<Button
key="resynchronizeBtn"
text
type="primary"
onClick={() => this.resynchronize(record)}
>{locale.resynchronizeBtn}
</Button>,
);
buttonList.push(
<Button
key="deleteBtn"
text
type="primary"
style={{ marginLeft: 18 }}
onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}
</Button>,
);
}
return buttonList;
}}
/>
</Table>
{
totalPage > 1 && (
<Pagination
onChange={currentPageNum => this.turnPage(currentPageNum)}
current={pageNum}
total={totalSize}
className="list-pagination"
/>
)
}
}
componentDidMount() {
const {pageNum} = this.state
const {list} = this.props
list({pageNum}).then(() => this.loadComplete())
}
loadComplete() {
this.setState({loading: false})
}
turnPage(pageNum) {
const {search} = this.state
const {list} = this.props
this.setState({pageNum, loading: true}, () => list({pageNum, ...search}).then(() => this.loadComplete()))
}
deleteServiceSync(record) {
const {taskId} = record
const {locale = {}} = this.props
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => update({taskId, taskStatus: 'DELETE'}).then(() => {
this.turnPage(this.state.pageNum)
Message.success(locale.successMsg)
})
})
}
resynchronize(record) {
const {taskId} = record
const {locale = {}} = this.props
update({taskId, taskStatus: 'SYNC'}).then(() => {
this.turnPage(this.state.pageNum)
Message.success(locale.syncSuccessMsg)
})
}
onChangeSearchForm(obj) {
const {search} = this.state
this.setState({search: Object.assign({}, search, obj)})
}
openAddDialog() {
this.addDialog.current.getInstance().open()
}
render() {
const {loading, pageNum, search} = this.state
const {taskModels = [], locale = {}, totalSize = 0, totalPage = 0} = this.props
return (
<div className="service-sync">
<FuncHead title={locale.title}/>
<Form inline className="search-form">
<FormItem label={`${locale.serviceName}:`}>
<Input
style={{width: 198}}
value={search.serviceName}
placeholder={locale.serviceNamePlaceholder}
onChange={serviceName => this.onChangeSearchForm({serviceName})}
/>
</FormItem>
<Button type="primary" onClick={() => this.turnPage(1)}>{locale.search}</Button>
<Button
type="normal"
className="add-btn"
onClick={() => this.openAddDialog()}
>{locale.addSync}</Button>
</Form>
<Table dataSource={taskModels} loading={loading}>
<Table.Column title={locale.serviceName} dataIndex="serviceName"/>
<Table.Column title={locale.groupName} dataIndex="groupName"/>
<Table.Column title={locale.sourceCluster} dataIndex="sourceClusterName"/>
<Table.Column title={locale.destCluster} dataIndex="destClusterName"/>
<Table.Column
title={locale.operation}
cell={(value, index, record) => {
if (record.taskStatus === 'SYNC') {
return (
<Button
text
type="primary"
onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}</Button>
)
}
if (record.taskStatus === 'DELETE') {
return (
<Button
text
type="primary"
onClick={() => this.resynchronize(record)}
>{locale.resynchronizeBtn}</Button>
)
}
}}
/>
</Table>
{
totalPage > 1
? <Pagination
onChange={pageNum => this.turnPage(pageNum)}
current={pageNum}
total={totalSize}
className="list-pagination"
/>
: null
}
<AddSyncDialog ref={this.addDialog}/>
</div>
)
}
<AddSyncDialog ref={this.addDialog} turnPage={pn => this.turnPage(pn)} />
</div>
);
}
}
export default ServiceSync
export default ServiceSync;

View File

@ -1,3 +1,3 @@
import ServiceSync from './ServiceSync'
import ServiceSync from './ServiceSync';
export default ServiceSync
export default ServiceSync;

View File

@ -1,50 +1,52 @@
import React from 'react'
import {Form, Input, Dialog, ConfigProvider} from '@alifd/next'
import '../../style/dialog-form.scss'
import React from 'react';
import { Form, Input, Dialog, ConfigProvider } from '@alifd/next';
import '../../style/dialog-form.scss';
const FormItem = Form.Item
const FormItem = Form.Item;
@ConfigProvider.config
class AddSysConfigDialog extends React.Component {
static displayName = 'AddSysConfigDialog'
constructor(props) {
super(props)
this.state = {
visible: false
}
super(props);
this.state = {
visible: false,
};
}
save() {
this.close()
console.log('save...')
this.close();
}
close() {
this.setState({visible: false})
this.setState({ visible: false });
}
open = () => this.setState({visible: true})
open = () => this.setState({ visible: true })
render() {
const {locale = {}} = this.props
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}>
<Form>
<FormItem label={`${locale.configName}:`}>
<Input placeholder={locale.configNamePlaceholder}/>
</FormItem>
<FormItem label={`${locale.configValue}:`}>
<Input placeholder={locale.configValuePlaceholder}/>
</FormItem>
</Form>
</Dialog>
)
const { locale = {} } = this.props;
return (
<Dialog
className="dialog-form"
title={locale.title}
visible={this.state.visible}
onOk={() => this.save()}
onCancel={() => this.close()}
onClose={() => this.close()}
>
<Form>
<FormItem label={`${locale.configName}:`}>
<Input placeholder={locale.configNamePlaceholder} />
</FormItem>
<FormItem label={`${locale.configValue}:`}>
<Input placeholder={locale.configValuePlaceholder} />
</FormItem>
</Form>
</Dialog>
);
}
}
export default AddSysConfigDialog
export default AddSysConfigDialog;

View File

@ -1,70 +1,73 @@
import React from 'react'
import {Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider} from '@alifd/next'
import FuncHead from '../../components/FuncHead'
import AddSysConfigDialog from './AddSysConfigDialog'
import './index.scss'
import React from 'react';
import { Table, Pagination, Form, Input, Button, Dialog, Message, ConfigProvider } from '@alifd/next';
import FuncHead from '../../components/FuncHead';
import AddSysConfigDialog from './AddSysConfigDialog';
import './index.scss';
const FormItem = Form.Item
const FormItem = Form.Item;
const dataSource = []
for (let i = 0; i < 10; i++) {
dataSource.push({
c1: 'key1',
c2: '1',
})
}
const dataSource = [];
for (let i = 0; i < 10; i++) dataSource.push({ c1: 'key1', c2: '1' });
@ConfigProvider.config
class SystemConfig extends React.Component {
constructor(props) {
super(props)
this.addDialog = React.createRef()
}
static displayName = 'SystemConfig'
deleteServiceSync() {
const {locale = {}} = this.props
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => setTimeout(() => Message.success(locale.successMsg), 888),
onCancel: () => console.log('cancel')
})
}
constructor(props) {
super(props);
this.addDialog = React.createRef();
}
openAddDialog() {
this.addDialog.current.getInstance().open()
}
deleteServiceSync() {
const { locale = {} } = this.props;
Dialog.confirm({
title: locale.confirm,
content: locale.confirmMsg,
onOk: () => setTimeout(() => Message.success(locale.successMsg), 888),
});
}
render() {
const {locale = {}} = this.props
return (
<div className="system-config">
<FuncHead title={locale.title}/>
<Form inline className="search-form">
<FormItem label={`${locale.configName}:`}>
<Input style={{width: 198}} placeholder={locale.configNamePlaceholder}/>
</FormItem>
<Button type="primary">{locale.search}</Button>
<Button type="normal" className="add-btn"
onClick={() => this.openAddDialog()}>{locale.addConfig}</Button>
</Form>
<Table dataSource={dataSource}>
<Table.Column title={locale.configName} dataIndex="c1"/>
<Table.Column title={locale.value} dataIndex="c2"/>
<Table.Column
title={locale.operation}
cell={(value, index, record) => (
<Button type="primary" text onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}</Button>
)}
/>
</Table>
<Pagination onChange={f => f} className="list-pagination"/>
<AddSysConfigDialog ref={this.addDialog}/>
</div>
)
}
openAddDialog() {
this.addDialog.current.getInstance().open();
}
render() {
const { locale = {} } = this.props;
return (
<div className="system-config">
<FuncHead title={locale.title} />
<Form inline className="search-form">
<FormItem label={`${locale.configName}:`}>
<Input style={{ width: 198 }} placeholder={locale.configNamePlaceholder} />
</FormItem>
<Button type="primary">{locale.search}</Button>
<Button
type="normal"
className="add-btn"
onClick={() => this.openAddDialog()}
>{locale.addConfig}
</Button>
</Form>
<Table dataSource={dataSource}>
<Table.Column title={locale.configName} dataIndex="c1" />
<Table.Column title={locale.value} dataIndex="c2" />
<Table.Column
title={locale.operation}
cell={(value, index, record) => (
<Button
type="primary"
text
onClick={() => this.deleteServiceSync(record)}
>{locale.deleteBtn}
</Button>
)}
/>
</Table>
<Pagination onChange={f => f} className="list-pagination" />
<AddSysConfigDialog ref={this.addDialog} />
</div>
);
}
}
export default SystemConfig
export default SystemConfig;

View File

@ -1,3 +1,3 @@
import SystemConfig from './SystemConfig'
import SystemConfig from './SystemConfig';
export default SystemConfig
export default SystemConfig;

View File

@ -1,75 +1,74 @@
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore, combineReducers, compose, applyMiddleware} from 'redux'
import {routerReducer} from 'react-router-redux'
import thunk from 'redux-thunk'
import {Provider, connect} from 'react-redux'
import {HashRouter, Route, Switch, Redirect} from 'react-router-dom'
import {ConfigProvider} from "@alifd/next"
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers, compose, applyMiddleware } from 'redux';
import { routerReducer } from 'react-router-redux';
import thunk from 'redux-thunk';
import { Provider, connect } from 'react-redux';
import { HashRouter, Route, Switch, Redirect } from 'react-router-dom';
import { ConfigProvider } from '@alifd/next';
import Layout from './containers/Layout'
import ServiceSync from './containers/ServiceSync'
import SystemConfig from './containers/SystemConfig'
import ClusterConfig from './containers/ClusterConfig'
import CookieHelp from './utils/cookie'
import {LANGUAGE_KEY} from './constants'
import Layout from './containers/Layout';
import ServiceSync from './containers/ServiceSync';
// import SystemConfig from './containers/SystemConfig';
import ClusterConfig from './containers/ClusterConfig';
import { LANGUAGE_KEY, REDUX_DEVTOOLS } from './constants';
import * as reducers from './reducers'
import {changeLanguage} from "./reducers/locale"
import * as reducers from './reducers';
import { changeLanguage } from './reducers/locale';
import './index.scss'
import './index.scss';
if (!CookieHelp.getValue(LANGUAGE_KEY)) {
CookieHelp.setValue(LANGUAGE_KEY, navigator.language === 'zh-CN' ? 'zh-CN' : 'en-US')
if (!localStorage.getItem(LANGUAGE_KEY)) {
localStorage.setItem(LANGUAGE_KEY, navigator.language === 'zh-CN' ? 'zh-CN' : 'en-US');
}
const reducer = combineReducers({
...reducers,
routing: routerReducer
})
...reducers,
routing: routerReducer,
});
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
)
reducer,
compose(
applyMiddleware(thunk),
window[REDUX_DEVTOOLS] ? window[REDUX_DEVTOOLS]() : f => f,
),
);
@connect(state => ({...state.locale}), {changeLanguage})
const MENU = [
{ path: '/', exact: true, render: () => (<Redirect to="/serviceSync" />) },
{ path: '/serviceSync', component: ServiceSync },
{ path: '/clusterConfig', component: ClusterConfig },
// { path: '/systemConfig', component: SystemConfig },
];
@connect(state => ({ ...state.locale }), { changeLanguage })
class App extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
const language = localStorage.getItem(LANGUAGE_KEY);
this.props.changeLanguage(language);
}
componentDidMount() {
const language = CookieHelp.getValue(LANGUAGE_KEY)
this.props.changeLanguage(language)
}
get router() {
return (
<HashRouter>
<Layout>
<Switch>
{MENU.map(item => (<Route {...Object.assign(item, { key: item.path })} />))}
</Switch>
</Layout>
</HashRouter>
);
}
generateRouter() {
return (
<HashRouter>
<Layout>
<Switch>
<Route path='/' exact render={() => <Redirect to="/serviceSync"/>}/>
<Route path="/serviceSync" component={ServiceSync}/>
<Route path="/systemConfig" component={SystemConfig}/>
<Route path="/clusterConfig" component={ClusterConfig}/>
</Switch>
</Layout>
</HashRouter>
)
}
render() {
const {locale} = this.props
return (
<ConfigProvider locale={locale}>
{this.generateRouter()}
</ConfigProvider>
)
}
render() {
const { locale } = this.props;
return (
<ConfigProvider locale={locale}>
{this.router}
</ConfigProvider>
);
}
}
ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById('root'))
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));

View File

@ -1,84 +1,94 @@
const I18N_CONF = {
Header: {
home: 'HOME',
docs: 'DOCS',
blog: 'BLOG',
community: 'COMMUNITY',
languageSwitchButton: '中'
},
Menu: {
serviceSync: 'Service Synchronization',
clusterConfig: 'Cluster Configuration',
systemConfig: 'System configuration',
},
ClusterConfig: {
title: 'Cluster Configuration',
addCluster: 'New Cluster',
search: 'Search',
clusterName: 'Cluster Name',
clusterNamePlaceholder: 'Please enter the cluster name',
clusterType: 'Cluster Type',
connectKeyList: 'Connect Key List',
operation: 'Operation',
deleteBtn: 'Delete',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
successMsg: 'Delete the success!',
},
AddConfigDialog: {
title: 'New Cluster',
clusterName: 'Cluster Name',
clusterNamePlaceholder: 'Please enter the cluster name',
clusterType: 'Cluster Type',
connectKeyList: 'Connect IP',
connectKeyListPlaceholder: 'Please enter the ip'
},
ServiceSync: {
title: 'Service Synchronization',
serviceNamePlaceholder: 'Please enter service name',
search: 'Search',
addSync: 'New Sync',
serviceName: 'Service Name',
groupName: 'Group',
sourceCluster: 'Source Cluster',
destCluster: 'Dest Cluster',
instancesCount: 'Instances Count',
operation: 'Operation',
deleteBtn: 'Delete',
resynchronizeBtn: 'Resynchronize',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
successMsg: 'Delete the success!',
syncSuccessMsg: 'Resync succeeded!',
},
AddSyncDialog: {
title: 'New Sync',
serviceName: 'Service Name',
serviceNamePlaceholder: 'Please enter service name',
groupName: 'Group Name',
groupNamePlaceholder: 'Please enter group name',
sourceCluster: 'Source Cluster',
destCluster: 'Dest Cluster'
},
SystemConfig: {
title: 'System configuration',
configName: 'Config Name',
configNamePlaceholder: 'Please enter config name',
search: 'Search',
addConfig: 'New SysConfig',
value: 'Value',
operation: 'Operation',
deleteBtn: 'Delete',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
successMsg: 'Delete the success!',
},
AddSysConfigDialog: {
title: 'New System configuration',
configName: 'Config Name',
configNamePlaceholder: 'Please enter config name',
configValue: 'Config Value',
configValuePlaceholder: 'Please enter config value',
}
}
export default I18N_CONF
Header: {
home: 'HOME',
docs: 'DOCS',
blog: 'BLOG',
community: 'COMMUNITY',
languageSwitchButton: '中',
},
Menu: {
serviceSync: 'Service Synchronization',
clusterConfig: 'Cluster Configuration',
systemConfig: 'System configuration',
},
ClusterConfig: {
title: 'Cluster Configuration',
addCluster: 'New Cluster',
search: 'Search',
clusterName: 'Cluster Name',
clusterNamePlaceholder: 'Please enter the cluster name',
clusterType: 'Cluster Type',
connectKeyList: 'Connect Key List',
namespace: 'Namespace',
password: 'Password',
username: 'Username',
operation: 'Operation',
deleteBtn: 'Delete',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
successMsg: 'Delete the success!',
},
AddConfigDialog: {
title: 'New Cluster',
clusterName: 'Cluster Name',
clusterNamePlaceholder: 'Please enter the cluster name',
namespace: 'Namespace',
namespacePlaceholder: 'Please enter the namespace',
clusterType: 'Cluster Type',
connectKeyList: 'Connect IP',
connectKeyListPlaceholder: 'Please enter the ip',
},
ServiceSync: {
title: 'Service Synchronization',
serviceNamePlaceholder: 'Please enter service name',
search: 'Search',
addSync: 'New Sync',
serviceName: 'Service Name',
groupName: 'Group',
sourceCluster: 'Source Cluster',
destCluster: 'Dest Cluster',
instancesCount: 'Instances Count',
operation: 'Operation',
deleteBtn: 'Delete',
suspendedBtn: 'Suspend',
resynchronizeBtn: 'Resynchronize',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
suspendedMsg: 'Are you sure you want to pause this row',
successMsg: 'Suspension of success!',
deleteSuccessMsg: 'Delete the success!',
syncSuccessMsg: 'Resync succeeded!',
},
AddSyncDialog: {
title: 'New Sync',
serviceName: 'Service Name',
serviceNamePlaceholder: 'Please enter service name',
groupName: 'Group Name',
groupNamePlaceholder: 'Please enter group name',
sourceCluster: 'Source Cluster',
destCluster: 'Dest Cluster',
version: 'Version',
versionPlaceholder: 'Please enter version',
},
SystemConfig: {
title: 'System configuration',
configName: 'Config Name',
configNamePlaceholder: 'Please enter config name',
search: 'Search',
addConfig: 'New SysConfig',
value: 'Value',
operation: 'Operation',
deleteBtn: 'Delete',
confirm: 'Prompt',
confirmMsg: 'Are you sure you want to delete this row',
successMsg: 'Delete the success!',
},
AddSysConfigDialog: {
title: 'New System configuration',
configName: 'Config Name',
configNamePlaceholder: 'Please enter config name',
configValue: 'Config Value',
configValuePlaceholder: 'Please enter config value',
},
};
export default I18N_CONF;

View File

@ -1,4 +1,4 @@
import enUS from './en-US'
import zhCN from './zh-CN'
import enUS from './en-US';
import zhCN from './zh-CN';
export default {enUS, zhCN}
export default { enUS, zhCN };

View File

@ -1,84 +1,96 @@
const I18N_CONF = {
Header: {
home: '首页',
docs: '文档',
blog: '博客',
community: '社区',
languageSwitchButton: 'En'
},
Menu: {
serviceSync: '服务同步',
clusterConfig: '集群配置',
systemConfig: '系统配置',
},
ClusterConfig: {
title: '集群配置',
addCluster: '新增集群',
search: '搜索',
clusterName: '集群名',
clusterNamePlaceholder: '请输入集群名',
clusterType: '集群类型',
connectKeyList: '集群IP列表',
operation: '操作',
deleteBtn: '删除',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
successMsg: '删除成功!',
},
AddConfigDialog: {
title: '新增集群',
clusterName: '集群名',
clusterNamePlaceholder: '请输入集群名',
clusterType: '集群类型',
connectKeyList: '集群IP列表',
connectKeyListPlaceholder: '请输入集群IP'
},
ServiceSync: {
title: '同步服务',
serviceNamePlaceholder: '请输入服务名',
search: '搜索',
addSync: '新增同步',
serviceName: '服务名',
groupName: '分组',
sourceCluster: '源集群',
destCluster: '目标集群',
instancesCount: '实例数',
operation: '操作',
deleteBtn: '删除',
resynchronizeBtn: '重新同步',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
successMsg: '删除成功!',
syncSuccessMsg: '重新同步成功!',
},
AddSyncDialog: {
title: '新增同步',
serviceName: '服务名',
serviceNamePlaceholder: '请输入服务名',
groupName: '分组名',
groupNamePlaceholder: '请输入分组名',
sourceCluster: '源集群',
destCluster: '目标集群'
},
SystemConfig: {
title: '系统配置',
configName: '配置名',
configNamePlaceholder: '请输入配置名',
search: '搜索',
addConfig: '新增系统配置',
value: '值',
operation: '操作',
deleteBtn: '删除',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
successMsg: '删除成功!'
},
AddSysConfigDialog: {
title: '新增系统配置',
configName: '配置名',
configNamePlaceholder: '请输入配置名',
configValue: '配置值',
configValuePlaceholder: '请输入配置值',
}
}
export default I18N_CONF
Header: {
home: '首页',
docs: '文档',
blog: '博客',
community: '社区',
languageSwitchButton: 'En',
},
Menu: {
serviceSync: '服务同步',
clusterConfig: '集群配置',
systemConfig: '系统配置',
},
ClusterConfig: {
title: '集群配置',
addCluster: '新增集群',
search: '搜索',
clusterName: '集群名',
clusterNamePlaceholder: '请输入集群名',
clusterType: '集群类型',
connectKeyList: '集群IP列表',
namespace: '命名空间',
operation: '操作',
deleteBtn: '删除',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
successMsg: '删除成功!',
},
AddConfigDialog: {
title: '新增集群',
clusterName: '集群名',
clusterNamePlaceholder: '请输入集群名',
namespace: '命名空间',
namespacePlaceholder: '请输入命名空间',
password: '密码',
passwordPlaceholder: '请输入密码',
username: '用户名',
usernamePlaceholder: '请输入用户名',
clusterType: '集群类型',
connectKeyList: '集群IP列表',
connectKeyListPlaceholder: '请输入集群IP',
},
ServiceSync: {
title: '同步服务',
serviceNamePlaceholder: '请输入服务名',
search: '搜索',
addSync: '新增同步',
serviceName: '服务名',
groupName: '分组',
sourceCluster: '源集群',
destCluster: '目标集群',
instancesCount: '实例数',
operation: '操作',
deleteBtn: '删除',
suspendedBtn: '暂停',
resynchronizeBtn: '重新同步',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
suspendedMsg: '确定要暂停这行数据吗?',
successMsg: '暂停成功!',
deleteSuccessMsg: '删除成功!',
syncSuccessMsg: '重新同步成功!',
},
AddSyncDialog: {
title: '新增同步',
serviceName: '服务名',
serviceNamePlaceholder: '请输入服务名',
groupName: '分组名',
groupNamePlaceholder: '请输入分组名',
sourceCluster: '源集群',
destCluster: '目标集群',
version: '版本',
versionPlaceholder: '请输入版本',
},
SystemConfig: {
title: '系统配置',
configName: '配置名',
configNamePlaceholder: '请输入配置名',
search: '搜索',
addConfig: '新增系统配置',
value: '值',
operation: '操作',
deleteBtn: '删除',
confirm: '提示',
confirmMsg: '确定要删除这行数据吗?',
successMsg: '删除成功!',
},
AddSysConfigDialog: {
title: '新增系统配置',
configName: '配置名',
configNamePlaceholder: '请输入配置名',
configValue: '配置值',
configValuePlaceholder: '请输入配置值',
},
};
export default I18N_CONF;

View File

@ -1,52 +1,50 @@
import request from '../utils/request'
import {CLUSTER_LIST, GET_CLUSTER_BY_ID, CLUSTER_TYPES, PAGE_SIZE} from '../constants'
import request from '../utils/request';
import { CLUSTER_LIST, GET_CLUSTER_BY_ID, CLUSTER_TYPES, PAGE_SIZE } from '../constants';
const PATH = '/nacossync/v1/cluster/'
const PATH = '/v1/cluster/';
const initialState = {
totalPage: 0,
totalSize: 0,
clusterModel: {},
clusterModels: [],
types: []
}
totalPage: 0,
totalSize: 0,
clusterModel: {},
clusterModels: [],
types: [],
};
const add = data => request.post(`${PATH}add`, data)
const add = data => request.post(`${PATH}add`, data);
const deleteCluster = data => request.delete(`${PATH}delete`, {data})
const deleteCluster = params => request.delete(`${PATH}delete`, { params });
const list = params => dispatch => request.get(`${PATH}list`, {
params: {
...params,
pageSize: PAGE_SIZE
}
params: {
pageSize: PAGE_SIZE,
...params,
},
}).then(data => dispatch({
type: CLUSTER_LIST,
data
}))
type: CLUSTER_LIST,
data,
}));
const detail = params => dispatch => request.get(`${PATH}detail`, {params}).then(({clusterModel}) => dispatch({
type: GET_CLUSTER_BY_ID,
data: clusterModel
})
)
const detail = params => dispatch => request.get(`${PATH}detail`, { params }).then(({ clusterModel }) => dispatch({
type: GET_CLUSTER_BY_ID,
data: clusterModel,
}));
const getTypes = () => dispatch => request.get(`${PATH}types`).then(({types}) => dispatch({
type: CLUSTER_TYPES,
data: types
})
)
const getTypes = () => dispatch => request.get(`${PATH}types`).then(({ types }) => dispatch({
type: CLUSTER_TYPES,
data: types,
}));
export default (state = initialState, action) => {
switch (action.type) {
case CLUSTER_LIST:
return {...state, ...action.data}
case GET_CLUSTER_BY_ID:
return {...state, clusterModel: action.data}
case CLUSTER_TYPES:
return {...state, types: action.data}
default:
return state
}
}
switch (action.type) {
case CLUSTER_LIST:
return { ...state, ...action.data };
case GET_CLUSTER_BY_ID:
return { ...state, clusterModel: action.data };
case CLUSTER_TYPES:
return { ...state, types: action.data };
default:
return state;
}
};
export {list, add, deleteCluster, detail, getTypes}
export { list, add, deleteCluster, detail, getTypes };

View File

@ -1,5 +1,5 @@
import locale from './locale'
import cluster from './cluster'
import task from './task'
import locale from './locale';
import cluster from './cluster';
import task from './task';
export {locale, cluster, task}
export { locale, cluster, task };

View File

@ -1,30 +1,29 @@
import fusionEnUS from '@alifd/next/lib/locale/en-us'
import fusionZhCN from '@alifd/next/lib/locale/zh-cn'
import I18N from '../locales'
import {LANGUAGE_KEY, LANGUAGE_SWITCH} from "../constants";
import CookieHelp from "../utils/cookie";
import fusionEnUS from '@alifd/next/lib/locale/en-us';
import fusionZhCN from '@alifd/next/lib/locale/zh-cn';
import I18N from '../locales';
import { LANGUAGE_KEY, LANGUAGE_SWITCH } from '../constants';
const enUS = Object.assign({}, fusionEnUS, I18N.enUS)
const zhCN = Object.assign({}, fusionZhCN, I18N.zhCN)
const enUS = Object.assign({}, fusionEnUS, I18N.enUS);
const zhCN = Object.assign({}, fusionZhCN, I18N.zhCN);
const initialState = {
language: 'en-US',
locale: enUS
}
language: 'en-US',
locale: enUS,
};
const changeLanguage = language => dispatch => {
language = language === 'zh-CN' ? 'zh-CN' : 'en-US'
CookieHelp.setValue(LANGUAGE_KEY, language)
dispatch({type: LANGUAGE_SWITCH, language, locale: language === 'zh-CN' ? zhCN : enUS})
}
const changeLanguage = language => (dispatch) => {
const newLan = language === 'zh-CN' ? 'zh-CN' : 'en-US';
localStorage.setItem(LANGUAGE_KEY, newLan);
dispatch({ type: LANGUAGE_SWITCH, language: newLan, locale: newLan === 'zh-CN' ? zhCN : enUS });
};
export default (state = initialState, action) => {
switch (action.type) {
case LANGUAGE_SWITCH:
return {...state, ...action}
default:
return state
}
}
switch (action.type) {
case LANGUAGE_SWITCH:
return { ...state, ...action };
default:
return state;
}
};
export {changeLanguage}
export { changeLanguage };

View File

@ -1,44 +1,45 @@
import request from '../utils/request'
import {TASK_LIST, GET_TASK_BY_ID, PAGE_SIZE} from '../constants'
import request from '../utils/request';
import { TASK_LIST, GET_TASK_BY_ID, PAGE_SIZE } from '../constants';
const PATH = '/nacossync/v1/task/'
const PATH = '/v1/task/';
const initialState = {
totalPage: 0,
totalSize: 0,
taskModel: {},
taskModels: [],
types: []
}
totalPage: 0,
totalSize: 0,
taskModel: {},
taskModels: [],
types: [],
};
const add = data => request.post(`${PATH}add`, data)
const add = data => request.post(`${PATH}add`, data);
const update = data => request.post(`${PATH}update`, data)
const update = data => request.post(`${PATH}update`, data);
const deleteRow = params => request.delete(`${PATH}delete`, { params });
const list = params => dispatch => request.get(`${PATH}list`, {
params: {
...params,
pageSize: PAGE_SIZE
}
params: {
pageSize: PAGE_SIZE,
...params,
},
}).then(data => dispatch({
type: TASK_LIST,
data
}))
type: TASK_LIST,
data,
}));
const detail = params => dispatch => request.get(`${PATH}detail`, {params}).then(({taskModel}) => dispatch({
type: GET_TASK_BY_ID,
data: taskModel
})
)
const detail = params => dispatch => request.get(`${PATH}detail`, { params }).then(({ taskModel }) => dispatch({
type: GET_TASK_BY_ID,
data: taskModel,
}));
export default (state = initialState, action) => {
switch (action.type) {
case TASK_LIST:
return {...state, ...action.data}
case GET_TASK_BY_ID:
return {...state, taskModel: action.data}
default:
return state
}
}
switch (action.type) {
case TASK_LIST:
return { ...state, ...action.data };
case GET_TASK_BY_ID:
return { ...state, taskModel: action.data };
default:
return state;
}
};
export {list, add, update, detail}
export { list, add, update, deleteRow, detail };

View File

@ -1,30 +1,30 @@
import axios from 'axios'
import {Message} from '@alifd/next'
import {SUCCESS_RESULT_CODE} from '../constants'
import axios from 'axios';
import { Message } from '@alifd/next';
import { SUCCESS_RESULT_CODE } from '../constants';
const API_GENERAL_ERROR_MESSAGE = 'Request error, please try again later!'
const API_GENERAL_ERROR_MESSAGE = 'Request error, please try again later!';
const request = () => {
const instance = axios.create()
const instance = axios.create();
instance.interceptors.response.use(response => {
const {success, resultCode, resultMessage = API_GENERAL_ERROR_MESSAGE} = response.data
if (!success && resultCode !== SUCCESS_RESULT_CODE) {
Message.error(resultMessage)
return Promise.reject(new Error(resultMessage))
}
return response.data
}, error => {
if (error.response) {
const {status} = error.response
Message.error(`HTTP ERROR: ${status}`)
} else {
Message.error(API_GENERAL_ERROR_MESSAGE)
}
return Promise.reject(error)
})
instance.interceptors.response.use((response) => {
const { success, resultCode, resultMessage = API_GENERAL_ERROR_MESSAGE } = response.data;
if (!success && resultCode !== SUCCESS_RESULT_CODE) {
Message.error(resultMessage);
return Promise.reject(new Error(resultMessage));
}
return response.data;
}, (error) => {
if (error.response) {
const { status } = error.response;
Message.error(`HTTP ERROR: ${status}`);
} else {
Message.error(API_GENERAL_ERROR_MESSAGE);
}
return Promise.reject(error);
});
return instance
}
return instance;
};
export default request()
export default request();

File diff suppressed because one or more lines are too long

View File

@ -5,10 +5,10 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Skywalker</title>
<title>Nacos-Sync</title>
<link rel="shortcut icon" href="//www.aliyun.com/favicon.ico" type="image/x-icon">
<link href="./css/main.8760bad6.css" rel="stylesheet"></head>
<link href="./css/main.e2917886.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="./js/main.ddff04aa.js"></script></body>
<script type="text/javascript" src="./js/main.b73436b5.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,334 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (properties) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
------
This product has a bundle logback, which is available under the EPL v1.0 License.
The source code of logback can be found at https://github.com/qos-ch/logback.
Logback LICENSE
---------------
Logback: the reliable, generic, fast and flexible logging framework.
Copyright (C) 1999-2015, QOS.ch. All rights reserved.
This program and the accompanying materials are dual-licensed under
either the terms of the Eclipse Public License v1.0 as published by
the Eclipse Foundation
or (per the licensee's choosing)
under the terms of the GNU Lesser General Public License version 2.1
as published by the Free Software Foundation.
------
This product has a bundle slf4j, which is available under the MIT License.
The source code of slf4j can be found at https://github.com/qos-ch/slf4j.
Copyright (c) 2004-2017 QOS.ch
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------
This product has a bundle fastjson, which is available under the ASL2 License.
The source code of fastjson can be found at https://github.com/alibaba/fastjson.
Copyright 1999-2016 Alibaba Group Holding Ltd.
Licensed 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.
------
This product has a bundle javassist, which is available under the ASL2 License.
The source code of javassist can be found at https://github.com/jboss-javassist/javassist.
Copyright (C) 1999- by Shigeru Chiba, All rights reserved.
Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation simple.
It is a class library for editing bytecodes in Java; it enables Java programs to define a new class
at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors,
Javassist provides two levels of API: source level and bytecode level. If the users use the source- level API,
they can edit a class file without knowledge of the specifications of the Java bytecode.
The whole API is designed with only the vocabulary of the Java language.
You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly.
On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.
This software is distributed under the Mozilla Public License Version 1.1,
the GNU Lesser General Public License Version 2.1 or later, or the Apache License Version 2.0.
------
This product has a bundle jna, which is available under the ASL2 License.
The source code of jna can be found at https://github.com/java-native-access/jna.
This copy of JNA is licensed under the
Apache (Software) License, version 2.0 ("the License").
See the License for details about distribution rights, and the
specific rights regarding derivate works.
You may obtain a copy of the License at:
http://www.apache.org/licenses/
A copy is also included in the downloadable source code package
containing JNA, in file "AL2.0", under the same directory
as this file.
------
This product has a bundle guava, which is available under the ASL2 License.
The source code of guava can be found at https://github.com/google/guava.
Copyright (C) 2007 The Guava authors
Licensed 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.
------
This product has a bundle OpenMessaging, which is available under the ASL2 License.
The source code of OpenMessaging can be found at https://github.com/openmessaging/openmessaging.
Copyright (C) 2017 The OpenMessaging authors.
Licensed 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.

View File

@ -0,0 +1,36 @@
Nacos
Copyright 2018-2019 The Apache Software Foundation
This product includes software developed at
The Alibaba MiddleWare Group.
------
This product has a bundle netty:
The Spring oot Project
=================
Please visit the Netty web site for more information:
* http://netty.io/
Copyright 2014 The Netty Project
The Netty Project 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.
Also, please refer to each LICENSE.<component>.txt file, which is located in
the 'license' directory of the distribution file, for the license terms of the
components that this product depends on.
------
This product has a bundle commons-lang, which includes software from the Spring Framework,
under the Apache License 2.0 (see: StringUtils.containsWhitespace())

View File

@ -0,0 +1,46 @@
/******************************************/
/* DB name = nacos_sync */
/* Table name = cluster */
/******************************************/
CREATE TABLE `cluster` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cluster_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`cluster_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`cluster_type` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`connect_key_list` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`user_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`namespace` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`cluster_level` int default 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
/******************************************/
/* DB name = nacos_sync */
/* Table name = system_config */
/******************************************/
CREATE TABLE `system_config` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`config_desc` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`config_key` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`config_value` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
/******************************************/
/* DB name = nacos_sync */
/* Table name = task */
/******************************************/
CREATE TABLE `task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dest_cluster_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`group_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`name_space` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`operation_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`service_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`source_cluster_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`task_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`task_status` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`version` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`worker_ip` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`status` int default null ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

View File

@ -0,0 +1,26 @@
#!/bin/sh
# Copyright 1999-2018 Alibaba Group Holding Ltd.
# Licensed 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.
pid=`ps ax | grep -i 'nacos-sync' |grep java | grep -v grep | awk '{print $1}'`
if [ -z "$pid" ] ; then
echo "no nacos-sync running."
exit -1;
fi
echo "the nacos-sync(${pid}) is running..."
kill ${pid}
echo "Send shutdown request to nacos-sync(${pid}) OK"

View File

@ -0,0 +1 @@
java -jar ../nacos-sync-server.jar -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m --spring.config.location=../conf/application.properties

View File

@ -0,0 +1,95 @@
#!/bin/bash
ACTION=$1
cygwin=false
darwin=false
os400=false
case "`uname`" in
CYGWIN*) cygwin=true;;
Darwin*) darwin=true;;
OS400*) os400=true;;
esac
error_exit ()
{
echo "ERROR: $1 !!"
exit 1
}
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
if [ -z "$JAVA_HOME" ]; then
if $darwin; then
if [ -x '/usr/libexec/java_home' ] ; then
export JAVA_HOME=`/usr/libexec/java_home`
elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
fi
else
JAVA_PATH=`dirname $(readlink -f $(which javac))`
if [ "x$JAVA_PATH" != "x" ]; then
export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null`
fi
fi
if [ -z "$JAVA_HOME" ]; then
error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"
fi
fi
export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export BASE_DIR=`cd $(dirname $0)/..; pwd`
JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/nacos-sync-java-heapdump.hprof"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
JAVA_OPT="${JAVA_OPT} -Dspring.config.location=${BASE_DIR}/conf/application.properties"
JAVA_OPT="${JAVA_OPT} -DnacosSync.home=${BASE_DIR}"
JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p')
if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then
JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${BASE_DIR}/logs/nacos-sync-gc.log:time,tags:filecount=10,filesize=102400"
else
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos-sync-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
fi
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/nacos-sync-server.jar"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/logback-spring.xml"
echo "JAVA_HOME:"$JAVA_HOME
echo "BASE_DIR:"$BASE_DIR
echo "JAVA:"$JAVA
if [ ! -d "${BASE_DIR}/logs" ]; then
mkdir "${BASE_DIR}/logs"
fi
usage(){
echo "command error"
}
start(){
echo "$JAVA ${JAVA_OPT}" > ${BASE_DIR}/logs/nacos-sync-start.out 2>&1 &
nohup $JAVA ${JAVA_OPT} >> ${BASE_DIR}/logs/nacos-sync-start.out 2>&1 &
echo "nacos-sync is startingyou can check the ${BASE_DIR}/logs/nacos-sync-start.out"
}
case "$ACTION" in
start)
start
;;
*)
usage
;;
esac

View File

View File

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacossync-parent</artifactId>
<groupId>com.alibaba.nacossync</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<artifactId>nacossync-distribution</artifactId>
<build>
<finalName>nacos-sync-${project.version}</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<!-- 打成zip包时id不加在文件名中 -->
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>release-nacossync.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 1999-2018 Alibaba Group Holding Ltd.
Licensed 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.
-->
<assembly>
<id>bin</id>
<includeBaseDirectory>true</includeBaseDirectory>
<!-- file name after unzip -->
<baseDirectory>nacos-sync</baseDirectory>
<formats>
<format>dir</format>
<format>tar.gz</format>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>../nacossync-worker/src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>application.properties</include>
<include>logback-spring.xml</include>
</includes>
</fileSet>
<fileSet>
<includes>
<include>conf/**</include>
</includes>
</fileSet>
<fileSet>
<includes>
<include>bin/**</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<includes>
<include>logs/**</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
<files>
<file>
<source>LICENSE-BIN</source>
<destName>LICENSE</destName>
</file>
<file>
<source>NOTICE-BIN</source>
<destName>NOTICE</destName>
</file>
<file>
<source>../nacossync-worker/target/nacos-sync-server-${project.version}.jar</source>
<outputDirectory></outputDirectory>
<destName>nacos-sync-server.jar</destName>
</file>
</files>
</assembly>

View File

@ -17,20 +17,52 @@
<parent>
<artifactId>nacossync-parent</artifactId>
<groupId>com.alibaba.nacossync</groupId>
<version>0.1.0</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>nacossync-test</artifactId>
<dependencies>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.0.0.RC2</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacossync-worker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<parallel>methods</parallel>
<useUnlimitedThreads>true</useUnlimitedThreads>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,566 @@
//package com.alibaba.nacossync;
//
//import java.util.HashMap;
//import java.util.Map;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.JSONArray;
//import com.alibaba.fastjson.JSONObject;
//import com.alibaba.nacossync.util.HttpClient;
//import com.alibaba.nacossync.util.HttpClient.HttpResult;
//
//import org.apache.http.HttpStatus;
//import org.junit.After;
//import org.junit.Assert;
//import org.junit.Before;
//import org.junit.Test;
//import org.junit.runner.RunWith;
//import org.springframework.boot.web.server.LocalServerPort;
//import org.springframework.test.context.junit4.SpringRunner;
//
//
///**
// * Created by mingyi.xxc
// * Date: 2019/2/23
// * Time: 下午7:20
// * DESC:
// *
// * @author mingyi.xxc
// * @date 2019/02/23
// */
//@RunWith(SpringRunner.class)
////@SpringBootTest(classes = NacosSyncMain.class, properties = {"server.servlet.context-path=/",
//// "server.port=8081"},
//// webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//public class RestApiTest {
//
// private String baseUrl;
//
// @LocalServerPort
// private int port;
//
// @Before
// public void setUp() throws Exception {
// this.baseUrl = String.format("http://localhost:%d", port);
// }
//
// @After
// public void cleanup() throws Exception {
//
// }
//
// @Test
// public void addCluster() {
// String url = baseUrl + "/v1/cluster/add";
//
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("clusterName", "CI-Nacos-Test" + System.currentTimeMillis());
// clusterJson.put("clusterType", "NACOS");
//
// JSONArray jsonArray = new JSONArray();
// jsonArray.add("11.11.11.11");
// jsonArray.add("22.22.22.22");
// clusterJson.put("connectKeyList", jsonArray);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void addClusterNotHaveClusterType() {
// String url = baseUrl + "/v1/cluster/add";
//
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("clusterName", "CI-Nacos-Test" + System.currentTimeMillis());
//
// JSONArray jsonArray = new JSONArray();
// jsonArray.add("11.11.11.11");
// jsonArray.add("22.22.22.22");
// clusterJson.put("connectKeyList", jsonArray);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertFalse(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void deleteCluster() {
// String url = baseUrl + "/v1/cluster/list";
// String clusterId = "";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "50");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// JSONArray clusterModels = JSON.parseObject(result.content).getJSONArray("clusterModels");
// if (clusterModels.size() > 0) {
// for (int i=0; i<clusterModels.size(); i++) {
// if (clusterModels.getJSONObject(i).getString("clusterName").startsWith("CI-Nacos-Test")) {
// clusterId = clusterModels.getJSONObject(i).getString("clusterId");
// System.out.println("find cluster id = " + clusterId);
// break;
// }
// }
// }
//
// url = baseUrl + "/v1/cluster/delete";
// paramValues = new HashMap<>();
// paramValues.put("clusterId", clusterId);
//
// result = HttpClient.httpDelete(url, paramValues, null, "UTF-8");
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// /**
// * delete clusterId不存在
// */
// @Test
// public void deleteClusterByClusterId() {
// String clusterId = "";
// try {
// String url = baseUrl + "/v1/cluster/delete";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("clusterId", clusterId);
//
// HttpResult result = HttpClient.httpDelete(url, paramValues, null, "UTF-8");
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getClusterDetails() {
// addCluster();
//
// String clusterId = "";
// String url = baseUrl + "/v1/cluster/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "50");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// JSONArray clusterModels = JSON.parseObject(result.content).getJSONArray("clusterModels");
// if (clusterModels.size() > 0) {
// for (int i=0; i<clusterModels.size(); i++) {
// if (clusterModels.getJSONObject(i).getString("clusterName").startsWith("CI-Nacos-Test")) {
// clusterId = clusterModels.getJSONObject(i).getString("clusterId");
// System.out.println("find cluster id = " + clusterId);
// break;
// }
// }
// }
//
// url = baseUrl + "/v1/cluster/detail";
// paramValues = new HashMap<>();
// paramValues.put("clusterId", clusterId);
//
// result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// Assert.assertEquals(clusterId, JSON.parseObject(result.content).getJSONObject("clusterModel").getString("clusterId"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getClusterList() {
// String url = baseUrl + "/v1/cluster/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "10");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// System.out.println(result.content);
// Assert.assertTrue(JSON.parseObject(result.content).getJSONArray("clusterModels").size() >= 0);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getClusterListByClusterName() {
// String url = baseUrl + "/v1/cluster/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "10");
// paramValues.put("clusterName", "-CI");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getJSONArray("clusterModels").size() == 0);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getClusterTypes() {
// String url = baseUrl + "/v1/cluster/types";
// try {
// HttpResult result = HttpClient.httpGet(url, null, null);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// System.out.println(result.content);
// Assert.assertTrue(JSON.parseObject(result.content).getJSONArray("types").contains("NACOS"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void addSystemConfig() {
// String url = baseUrl + "/v1/systemconfig/add";
//
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("config_desc", "test");
// clusterJson.put("config_key", "key");
// clusterJson.put("config_value", "value");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
//
// System.out.println(result.content);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void deleteSystemConfig() {
// String url = baseUrl + "/v1/systemconfig/delete";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("config_desc", "test");
// paramValues.put("config_key", "key");
// paramValues.put("config_value", "value");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpDelete(url, paramValues, null, "UTF-8");
// //Assert.assertEquals(HttpStatus.SC_OK, result.code);
// //Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getSystemConfigList() {
// String url = baseUrl + "/v1/systemconfig/list";
// try {
// HttpResult result = HttpClient.httpGet(url, null, null);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void addTask() {
// addCluster("EUREKA");
// addCluster("NACOS");
// String destClusterId = getClusterId("NACOS");
// String sourceClusterId = getClusterId("EUREKA");
//
// String url = baseUrl + "/v1/task/add";
//
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("destClusterId", destClusterId);
// clusterJson.put("groupName", "eureka");
// clusterJson.put("nameSpace", "CI-test");
// clusterJson.put("serviceName", "CI-Nacos-Service" + System.currentTimeMillis());
// clusterJson.put("sourceClusterId", sourceClusterId);
// clusterJson.put("version", "1.0.0");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void deleteTask() {
// String url = baseUrl + "/v1/task/delete";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("taskId", getTaskId());
//
// try {
// HttpClient.HttpResult result = HttpClient.httpDelete(url, paramValues, null, "UTF-8");
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void deleteTaskByTaskId() {
// String url = baseUrl + "/v1/task/delete";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("taskId", "");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpDelete(url, paramValues, null, "UTF-8");
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// System.out.println(result.content);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getTaskDetail() {
// String taskId = getTaskId();
// String url = baseUrl + "/v1/task/detail";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("taskId", taskId);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// Assert.assertEquals(taskId, JSON.parseObject(result.content).getJSONObject("taskModel").getString("taskId"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// /**
// * taskId为空, 必须存在taskId
// */
// @Test
// public void getTaskDetailWithTaskId() {
// String taskId = "";
// String url = baseUrl + "/v1/task/detail";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("taskId", taskId);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// System.out.print(result.content);
// Assert.assertFalse(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void getTaskList() {
// String url = baseUrl + "/v1/task/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "10");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
//
// Assert.assertTrue(JSON.parseObject(result.content).getJSONArray("taskModels").size() >= 0);
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void updateTask() {
// String url = baseUrl + "/v1/task/update";
// String taskId = getTaskId();
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("taskId", taskId);
// clusterJson.put("taskStatus", "DELETE");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// System.out.println(result.content);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// JSONObject taskDetail = getTaskDetailByTaskId(taskId);
// Assert.assertEquals("DELETE", taskDetail.getString("taskStatus"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// @Test
// public void updateTaskWithStatusSync() {
// String url = baseUrl + "/v1/task/update";
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("taskId", getTaskId());
// clusterJson.put("taskStatus", "SYNC");
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// System.out.println(result.content);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// /**
// * update中必须包含taskStatus
// */
// @Test
// public void updateTaskWithServiceName() {
// String url = baseUrl + "/v1/task/update";
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("taskId", getTaskId());
// String serviceName = "CI-Nacos-Service" + System.currentTimeMillis();
// clusterJson.put("serviceName", serviceName);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// System.out.println(result.content);
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertFalse(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// private void addCluster(String clusterType) {
// String url = baseUrl + "/v1/cluster/add";
//
// JSONObject clusterJson = new JSONObject();
// clusterJson.put("clusterName", "CI-Nacos-Test" + System.currentTimeMillis());
// clusterJson.put("clusterType", clusterType);
//
// JSONArray jsonArray = new JSONArray();
// jsonArray.add("11.11.11.11");
// jsonArray.add("22.22.22.22");
// clusterJson.put("connectKeyList", jsonArray);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpPost(url, clusterJson.toJSONString());
// Assert.assertEquals(HttpStatus.SC_OK, result.code);
// Assert.assertTrue(JSON.parseObject(result.content).getBoolean("success"));
// } catch (Exception e) {
// System.out.println(e.getMessage());
// Assert.assertTrue("error", false);
// }
// }
//
// private String getClusterId(String clusterType) {
// String clusterId = "";
// String url = baseUrl + "/v1/cluster/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "50");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// JSONArray clusterModels = JSON.parseObject(result.content).getJSONArray("clusterModels");
// if (clusterModels.size() > 0) {
// for (int i=0; i<clusterModels.size(); i++) {
// if (clusterModels.getJSONObject(i).getString("clusterName").startsWith("CI-Nacos-Test") && clusterModels.getJSONObject(i).getString("clusterType").equals(clusterType)) {
// clusterId = clusterModels.getJSONObject(i).getString("clusterId");
// System.out.println("find cluster id = " + clusterId);
// break;
// }
// }
// }
// } catch (Exception e) {
// System.out.println(e.getMessage());
// }
// return clusterId;
// }
//
// private String getTaskId() {
// String taskId = "";
// String url = baseUrl + "/v1/task/list";
// try {
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("pageNum", "1");
// paramValues.put("pageSize", "10");
//
// HttpResult result = HttpClient.httpGet(url, null, paramValues);
// JSONArray taskModels = JSON.parseObject(result.content).getJSONArray("taskModels");
//
// if (taskModels.size() > 0) {
// for (int i=0; i<taskModels.size(); i++) {
// if (taskModels.getJSONObject(i).getString("serviceName").startsWith("CI-Nacos-Service")) {
// taskId = taskModels.getJSONObject(i).getString("taskId");
// System.out.println("find task id = " + taskId);
// break;
// }
// }
// }
//
// } catch (Exception e) {
// System.out.println(e.getMessage());
// }
// return taskId;
// }
//
// private JSONObject getTaskDetailByTaskId(String taskId) {
// String url = baseUrl + "/v1/task/detail";
// Map<String, String> paramValues = new HashMap<>();
// paramValues.put("taskId", taskId);
//
// try {
// HttpClient.HttpResult result = HttpClient.httpGet(url, null, paramValues);
// if (HttpStatus.SC_OK == result.code) {
// return JSON.parseObject(result.content).getJSONObject("taskModel");
// }
// } catch (Exception e) {
// System.out.println(e.getMessage());
// return null;
// }
// return null;
// }
//
//}

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>nacossync-parent</artifactId>
<groupId>com.alibaba.nacossync</groupId>
<version>0.1.0</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacossync-worker</artifactId>
@ -24,19 +24,30 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- 默认使用HikariCP连接池 -->
<dependency>
@ -49,30 +60,123 @@
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--nacos-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>0.5.0</version>
</dependency>
<!--zookeeper-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- consul -->
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<finalName>nacos-sync-server-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>application.properties</exclude>
<exclude>logback-spring.xml</exclude>
</excludes>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -14,23 +14,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacossync;
import com.alibaba.nacossync.util.BatchTaskExecutor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author NacosSync
* @version $Id: SkyWalkerMain.java, v 0.1 2018-09-24 下午12:42 NacosSync Exp $$
* @version $Id: SkyWalkerMain.java, v 0.1 2018-09-24 PM12:42 NacosSync Exp $$
*/
@EnableSwagger2
@SpringBootApplication
@SpringBootApplication(exclude = EurekaClientAutoConfiguration.class)
public class NacosSyncMain {
public static void main(String[] args) {
SpringApplication.run(NacosSyncMain.class, args);
ConfigurableApplicationContext context = SpringApplication.run(NacosSyncMain.class, args);
// Register shutdown callback using Spring Boot's context lifecycle
context.registerShutdownHook();
context.addApplicationListener(event -> {
if (event instanceof org.springframework.context.event.ContextClosedEvent) {
BatchTaskExecutor.shutdown();
}
});
}
}

View File

@ -14,81 +14,85 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacossync.api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.pojo.result.*;
import com.alibaba.nacossync.pojo.request.ClusterAddRequest;
import com.alibaba.nacossync.pojo.request.ClusterDeleteRequest;
import com.alibaba.nacossync.pojo.request.ClusterDetailQueryRequest;
import com.alibaba.nacossync.pojo.request.ClusterListQueryRequest;
import com.alibaba.nacossync.pojo.result.ClusterAddResult;
import com.alibaba.nacossync.pojo.result.ClusterDeleteResult;
import com.alibaba.nacossync.pojo.result.ClusterDetailQueryResult;
import com.alibaba.nacossync.pojo.result.ClusterListQueryResult;
import com.alibaba.nacossync.pojo.result.ClusterTypeResult;
import com.alibaba.nacossync.template.SkyWalkerTemplate;
import com.alibaba.nacossync.template.processor.ClusterAddProcessor;
import com.alibaba.nacossync.template.processor.ClusterDeleteProcessor;
import com.alibaba.nacossync.template.processor.ClusterDetailQueryProcessor;
import com.alibaba.nacossync.template.processor.ClusterListQueryProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author NacosSync
* @version $Id: ClusterApi.java, v 0.1 2018-09-25 下午9:30 NacosSync Exp $$
* @version $Id: ClusterApi.java, v 0.1 2018-09-25 PM9:30 NacosSync Exp $$
*/
@Slf4j
@RestController
public class ClusterApi {
@Autowired
private ClusterAddProcessor clusterAddProcessor;
@Autowired
private ClusterDeleteProcessor clusterDeleteProcessor;
@Autowired
private ClusterDetailQueryProcessor clusterDetailQueryProcessor;
@Autowired
private ClusterListQueryProcessor clusterListQueryProcessor;
private final ClusterAddProcessor clusterAddProcessor;
private final ClusterDeleteProcessor clusterDeleteProcessor;
private final ClusterDetailQueryProcessor clusterDetailQueryProcessor;
private final ClusterListQueryProcessor clusterListQueryProcessor;
public ClusterApi(ClusterAddProcessor clusterAddProcessor, ClusterDeleteProcessor clusterDeleteProcessor,
ClusterDetailQueryProcessor clusterDetailQueryProcessor,
ClusterListQueryProcessor clusterListQueryProcessor) {
this.clusterAddProcessor = clusterAddProcessor;
this.clusterDeleteProcessor = clusterDeleteProcessor;
this.clusterDetailQueryProcessor = clusterDetailQueryProcessor;
this.clusterListQueryProcessor = clusterListQueryProcessor;
}
@RequestMapping(path = "/v1/cluster/list", method = RequestMethod.GET)
public ClusterListQueryResult clusters(ClusterListQueryRequest clusterListQueryRequest) {
return SkyWalkerTemplate.run(clusterListQueryProcessor, clusterListQueryRequest,
new ClusterListQueryResult());
return SkyWalkerTemplate.run(clusterListQueryProcessor, clusterListQueryRequest, new ClusterListQueryResult());
}
@RequestMapping(path = "/v1/cluster/detail", method = RequestMethod.GET)
public ClusterDetailQueryResult getByTaskId(ClusterDetailQueryRequest clusterDetailQueryRequest) {
return SkyWalkerTemplate.run(clusterDetailQueryProcessor, clusterDetailQueryRequest,
new ClusterDetailQueryResult());
}
@RequestMapping(path = "/v1/cluster/delete", method = RequestMethod.DELETE)
public ClusterDeleteResult deleteTask(@RequestBody ClusterDeleteRequest clusterDeleteRequest) {
return SkyWalkerTemplate.run(clusterDeleteProcessor, clusterDeleteRequest,
new ClusterDeleteResult());
public ClusterDeleteResult deleteCluster(ClusterDeleteRequest clusterDeleteRequest) {
return SkyWalkerTemplate.run(clusterDeleteProcessor, clusterDeleteRequest, new ClusterDeleteResult());
}
@RequestMapping(path = "/v1/cluster/add", method = RequestMethod.POST)
public ClusterAddResult clusterAdd(@RequestBody ClusterAddRequest clusterAddRequest) {
return SkyWalkerTemplate
.run(clusterAddProcessor, clusterAddRequest, new ClusterAddResult());
return SkyWalkerTemplate.run(clusterAddProcessor, clusterAddRequest, new ClusterAddResult());
}
@RequestMapping(path = "/v1/cluster/types", method = RequestMethod.GET)
public ClusterTypeResult getClusterType() {
return new ClusterTypeResult(ClusterTypeEnum.getClusterTypeCodes());
}
}

View File

@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacossync.api;
import lombok.extern.slf4j.Slf4j;
@ -37,39 +38,41 @@ import com.alibaba.nacossync.template.processor.ConfigQueryProcessor;
/**
* @author NacosSync
* @version $Id: SystemConfigApi.java, v 0.1 2018-09-26 上午2:06 NacosSync Exp $$
* @version $Id: SystemConfigApi.java, v 0.1 2018-09-26 AM2:06 NacosSync Exp $$
*/
@Slf4j
@RestController
public class SystemConfigApi {
@Autowired
private ConfigQueryProcessor configQueryProcessor;
@Autowired
private ConfigDeleteProcessor configDeleteProcessor;
@Autowired
private ConfigAddProcessor configAddProcessor;
private final ConfigQueryProcessor configQueryProcessor;
private final ConfigDeleteProcessor configDeleteProcessor;
private final ConfigAddProcessor configAddProcessor;
public SystemConfigApi(ConfigQueryProcessor configQueryProcessor, ConfigDeleteProcessor configDeleteProcessor,
ConfigAddProcessor configAddProcessor) {
this.configQueryProcessor = configQueryProcessor;
this.configDeleteProcessor = configDeleteProcessor;
this.configAddProcessor = configAddProcessor;
}
@RequestMapping(path = "/v1/systemconfig/list", method = RequestMethod.GET)
public ConfigQueryResult tasks(ConfigQueryRequest configQueryRequest) {
return SkyWalkerTemplate.run(configQueryProcessor, configQueryRequest,
new ConfigQueryResult());
return SkyWalkerTemplate.run(configQueryProcessor, configQueryRequest, new ConfigQueryResult());
}
@RequestMapping(path = "/v1/systemconfig/delete", method = RequestMethod.DELETE)
public ConfigDeleteResult deleteTask(@RequestBody ConfigDeleteRequest configDeleteRequest) {
return SkyWalkerTemplate.run(configDeleteProcessor, configDeleteRequest,
new ConfigDeleteResult());
return SkyWalkerTemplate.run(configDeleteProcessor, configDeleteRequest, new ConfigDeleteResult());
}
@RequestMapping(path = "/v1/systemconfig/add", method = RequestMethod.POST)
public ConfigAddResult taskAdd(@RequestBody ConfigAddRequest configAddRequest) {
return SkyWalkerTemplate.run(configAddProcessor, configAddRequest, new ConfigAddResult());
}
}

View File

@ -1,89 +1,127 @@
/*
* 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
* 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
* 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.
* 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.api;
import com.alibaba.nacossync.pojo.request.*;
import com.alibaba.nacossync.template.processor.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.nacossync.pojo.request.TaskAddAllRequest;
import com.alibaba.nacossync.pojo.request.TaskAddRequest;
import com.alibaba.nacossync.pojo.request.TaskDeleteInBatchRequest;
import com.alibaba.nacossync.pojo.request.TaskDeleteRequest;
import com.alibaba.nacossync.pojo.request.TaskDetailQueryRequest;
import com.alibaba.nacossync.pojo.request.TaskListQueryRequest;
import com.alibaba.nacossync.pojo.request.TaskUpdateRequest;
import com.alibaba.nacossync.pojo.result.BaseResult;
import com.alibaba.nacossync.pojo.result.TaskAddResult;
import com.alibaba.nacossync.pojo.result.TaskDetailQueryResult;
import com.alibaba.nacossync.pojo.result.TaskListQueryResult;
import com.alibaba.nacossync.template.SkyWalkerTemplate;
import com.alibaba.nacossync.template.processor.TaskAddAllProcessor;
import com.alibaba.nacossync.template.processor.TaskAddProcessor;
import com.alibaba.nacossync.template.processor.TaskDeleteInBatchProcessor;
import com.alibaba.nacossync.template.processor.TaskDeleteProcessor;
import com.alibaba.nacossync.template.processor.TaskDetailProcessor;
import com.alibaba.nacossync.template.processor.TaskListQueryProcessor;
import com.alibaba.nacossync.template.processor.TaskUpdateProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author NacosSync
* @version $Id: Task.java, v 0.1 2018-09-24 下午3:43 NacosSync Exp $$
* @version $Id: Task.java, v 0.1 2018-09-24 PM3:43 NacosSync Exp $$
*/
@Slf4j
@RestController
public class TaskApi {
@Autowired
private TaskUpdateProcessor taskUpdateProcessor;
@Autowired
private TaskAddProcessor taskAddProcessor;
@Autowired
private TaskDeleteProcessor taskDeleteProcessor;
@Autowired
private TaskListQueryProcessor taskListQueryProcessor;
@Autowired
private TaskDetailProcessor taskDetailProcessor;
private final TaskUpdateProcessor taskUpdateProcessor;
private final TaskAddProcessor taskAddProcessor;
private final TaskAddAllProcessor taskAddAllProcessor;
private final TaskDeleteProcessor taskDeleteProcessor;
private final TaskDeleteInBatchProcessor taskDeleteInBatchProcessor;
private final TaskListQueryProcessor taskListQueryProcessor;
private final TaskDetailProcessor taskDetailProcessor;
public TaskApi(TaskUpdateProcessor taskUpdateProcessor, TaskAddProcessor taskAddProcessor,
TaskAddAllProcessor taskAddAllProcessor, TaskDeleteProcessor taskDeleteProcessor,
TaskDeleteInBatchProcessor taskDeleteInBatchProcessor, TaskListQueryProcessor taskListQueryProcessor,
TaskDetailProcessor taskDetailProcessor) {
this.taskUpdateProcessor = taskUpdateProcessor;
this.taskAddProcessor = taskAddProcessor;
this.taskAddAllProcessor = taskAddAllProcessor;
this.taskDeleteProcessor = taskDeleteProcessor;
this.taskDeleteInBatchProcessor = taskDeleteInBatchProcessor;
this.taskListQueryProcessor = taskListQueryProcessor;
this.taskDetailProcessor = taskDetailProcessor;
}
@RequestMapping(path = "/v1/task/list", method = RequestMethod.GET)
public TaskListQueryResult tasks(TaskListQueryRequest taskListQueryRequest) {
return SkyWalkerTemplate.run(taskListQueryProcessor, taskListQueryRequest,
new TaskListQueryResult());
return SkyWalkerTemplate.run(taskListQueryProcessor, taskListQueryRequest, new TaskListQueryResult());
}
@RequestMapping(path = "/v1/task/detail", method = RequestMethod.GET)
public TaskDetailQueryResult getByTaskId(TaskDetailQueryRequest taskDetailQueryRequest) {
return SkyWalkerTemplate.run(taskDetailProcessor, taskDetailQueryRequest,
new TaskDetailQueryResult());
return SkyWalkerTemplate.run(taskDetailProcessor, taskDetailQueryRequest, new TaskDetailQueryResult());
}
@RequestMapping(path = "/v1/task/delete", method = RequestMethod.DELETE)
public BaseResult deleteTask(@RequestBody TaskDeleteRequest taskDeleteRequest) {
public BaseResult deleteTask(TaskDeleteRequest taskDeleteRequest) {
return SkyWalkerTemplate.run(taskDeleteProcessor, taskDeleteRequest, new BaseResult());
}
/**
* @param taskBatchDeleteRequest
* @return
* @author yongchao9
*/
@RequestMapping(path = "/v1/task/deleteInBatch", method = RequestMethod.DELETE)
public BaseResult batchDeleteTask(TaskDeleteInBatchRequest taskBatchDeleteRequest) {
return SkyWalkerTemplate.run(taskDeleteInBatchProcessor, taskBatchDeleteRequest, new BaseResult());
}
@RequestMapping(path = "/v1/task/add", method = RequestMethod.POST)
public BaseResult taskAdd(@RequestBody TaskAddRequest addTaskRequest) {
return SkyWalkerTemplate.run(taskAddProcessor, addTaskRequest, new TaskAddResult());
}
/**
* TODO 目前仅支持 Nacos 为源的同步类型待完善更多类型支持.
* <p>
* 支持从 sourceCluster 获取所有 service然后生成同步到 destCluster 的任务
* </p>
*/
@RequestMapping(path = "/v1/task/addAll", method = RequestMethod.POST)
public BaseResult taskAddAll(@RequestBody TaskAddAllRequest addAllRequest) {
return SkyWalkerTemplate.run(taskAddAllProcessor, addAllRequest, new TaskAddResult());
}
@RequestMapping(path = "/v1/task/update", method = RequestMethod.POST)
public BaseResult updateTask(@RequestBody TaskUpdateRequest taskUpdateRequest) {
return SkyWalkerTemplate.run(taskUpdateProcessor, taskUpdateRequest, new BaseResult());
}
}

View File

@ -16,19 +16,7 @@
*/
package com.alibaba.nacossync.cache;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.dao.ClusterAccessService;
import com.alibaba.nacossync.exception.SkyWalkerException;
@ -36,33 +24,51 @@ import com.alibaba.nacossync.pojo.FinishedTask;
import com.alibaba.nacossync.pojo.model.ClusterDO;
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.alibaba.nacossync.util.SkyWalkerUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
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 上午2:47 NacosSync Exp $$
* @version $Id: SkyWalkerCacheServices.java, v 0.1 2018-09-27 AM2:47 NacosSync Exp $$
*/
@Service
public class SkyWalkerCacheServices {
private static final Map<String, FinishedTask> FINISHED_TASK_MAP = new ConcurrentHashMap<>();
private final ClusterAccessService clusterAccessService;
@Autowired
private ClusterAccessService clusterAccessService;
private static Map<String, FinishedTask> finishedTaskMap = new ConcurrentHashMap<>();
private final ObjectMapper objectMapper;
public SkyWalkerCacheServices(ClusterAccessService clusterAccessService, ObjectMapper objectMapper) {
this.clusterAccessService = clusterAccessService;
this.objectMapper = objectMapper;
}
public String getClusterConnectKey(String clusterId) {
List<String> allClusterConnectKey = getAllClusterConnectKey(clusterId);
return allClusterConnectKey.get(ThreadLocalRandom.current().nextInt(allClusterConnectKey.size()));
}
ClusterDO clusterDOS = clusterAccessService.findByClusterId(clusterId);
@SneakyThrows
public List<String> getAllClusterConnectKey(String clusterId) {
ClusterDO clusterDO = clusterAccessService.findByClusterId(clusterId);
List<String> connectKeyList = JSONObject.parseObject(clusterDOS.getConnectKeyList(),
new TypeReference<List<String>>() {
});
List<String> connectKeyList = objectMapper.readerForListOf(String.class)
.readValue(clusterDO.getConnectKeyList());
if (CollectionUtils.isEmpty(connectKeyList)) {
throw new SkyWalkerException("getClusterConnectKey empty, clusterId:" + clusterId);
}
Random random = new Random();
return connectKeyList.get(random.nextInt(connectKeyList.size()));
return connectKeyList;
}
public ClusterTypeEnum getClusterType(String clusterId) {
@ -79,25 +85,32 @@ public class SkyWalkerCacheServices {
FinishedTask finishedTask = new FinishedTask();
finishedTask.setOperationId(operationId);
finishedTaskMap.put(operationId, finishedTask);
FINISHED_TASK_MAP.put(operationId, finishedTask);
}
public FinishedTask getFinishedTask(TaskDO taskDO) {
String operationId = SkyWalkerUtil.getOperationId(taskDO);
if (StringUtils.isEmpty(operationId)) {
if (!StringUtils.hasLength(operationId)) {
return null;
}
return finishedTaskMap.get(operationId);
return FINISHED_TASK_MAP.get(operationId);
}
public void removeFinishedTask(String operationId) {
if (!StringUtils.hasLength(operationId)) {
return;
}
FINISHED_TASK_MAP.remove(operationId);
}
public Map<String, FinishedTask> getFinishedTaskMap() {
return finishedTaskMap;
return FINISHED_TASK_MAP;
}
}

View File

@ -1,21 +1,19 @@
/*
* 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
* 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
* 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.
* 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.constant;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@ -23,26 +21,30 @@ import java.util.List;
* @author NacosSync
* @version $Id: ClusterTypeEnum.java, v 0.1 2018-09-25 下午4:38 NacosSync Exp $$
*/
@Getter
public enum ClusterTypeEnum {
CS("CS", "configserver集群"),
NACOS("NACOS", "nacos集群");
NACOS("NACOS", "nacos集群"),
//CONSUL("CONSUL", "consul集群");
EUREKA("EUREKA", "eureka集群"),
private String code;
CONSUL("CONSUL", "consul集群"),
ZK("ZK", "zookeeper集群");
private final String code;
private String desc;
ClusterTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static List<String> getClusterTypeCodes() {
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>();
for (ClusterTypeEnum clusterTypeEnum : ClusterTypeEnum.values()) {
list.add(clusterTypeEnum.getCode());
@ -50,42 +52,7 @@ public enum ClusterTypeEnum {
return list;
}
/**
* Getter method for property <tt>code</tt>.
*
* @return property value of code
*/
public String getCode() {
return code;
}
/**
* Setter method for property <tt>code </tt>.
*
* @param code value to be assigned to property code
*/
public void setCode(String code) {
this.code = code;
}
/**
* Getter method for property <tt>desc</tt>.
*
* @return property value of desc
*/
public String getDesc() {
return desc;
}
/**
* Setter method for property <tt>desc </tt>.
*
* @param desc value to be assigned to property desc
*/
public void setDesc(String desc) {
this.desc = desc;
}
public static boolean contains(String clusterType) {
for (ClusterTypeEnum clusterTypeEnum : ClusterTypeEnum.values()) {

View File

@ -0,0 +1,40 @@
/**
* Alipay.com Inc. Copyright (c) 2004-2019 All Rights Reserved.
*/
package com.alibaba.nacossync.constant;
import lombok.Getter;
/**
* @author NacosSync
* @version $Id: MetricsStatisticsType.java, v 0.1 2019年02月28日 下午2:17 NacosSync Exp $
*/
@Getter
public enum MetricsStatisticsType {
CACHE_SIZE("nacosSync.finished.taskMap.cacheSize", "任务执行完成缓存列表数"),
TASK_SIZE("nacosSync.task.size", "同步任务数"),
CLUSTER_SIZE("nacosSync.cluster.size", "集群数"),
SYNC_TASK_RT("nacosSync.add.task.rt", "同步任务执行耗时"),
DELETE_TASK_RT("nacosSync.delete.task.rt", "删除任务耗时"),
DISPATCHER_TASK("nacosSync.dispatcher.task", "从数据库中分发任务"),
SYNC_ERROR("nacosSync.sync.task.error", "所有同步执行时的异常"),
DELETE_ERROR("nacosSync.delete.task.error", "所有删除同步执行时的异常");
/**
* metricsName
*/
private final String metricsName;
MetricsStatisticsType(String code, String desc) {
this.metricsName = code;
}
}

View File

@ -16,46 +16,23 @@
*/
package com.alibaba.nacossync.constant;
import lombok.Getter;
/**
* @author NacosSync
* @version $Id: ResultCodeEnum.java, v 0.1 2018-09-25 下午4:38 NacosSync Exp $$
* @version $Id: ResultCodeEnum.java, v 0.1 2018-09-25 PM4:38 NacosSync Exp $$
*/
@Getter
public enum ResultCodeEnum {
SUCCESS("SUCCESS", "请求成功", "请求成功"),
SYSTEM_ERROR("SYSTEM_ERROR", "系统异常", "系统异常");
private String code;
private String errorMessage;
private String detail;
private final String code;
ResultCodeEnum(String code, String errorMessage, String detail) {
this.code = code;
this.errorMessage = errorMessage;
this.detail = detail;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}

View File

@ -18,15 +18,24 @@ package com.alibaba.nacossync.constant;
/**
* @author NacosSync
* @version $Id: SkyWalkerConstants.java, v 0.1 2018-09-26 上午12:07 NacosSync Exp $$
* @version $Id: SkyWalkerConstants.java, v 0.1 2018-09-26 AM12:07 NacosSync Exp $$
*/
public class SkyWalkerConstants {
public final static String UNDERLINE = "_";
public static final String UNDERLINE = "_";
public final static String DEST_CLUSTERID_KEY = "destClusterId";
public final static String GROUP_NAME = "groupName";
public final static String SYNC_SOURCE_KEY = "syncSource";
public final static String SOURCE_CLUSTERID_KEY = "sourceClusterId";
public static final String DEST_CLUSTER_ID_KEY = "destClusterId";
public static final String GROUP_NAME = "groupName";
public static final String SYNC_SOURCE_KEY = "syncSource";
public static final String SOURCE_CLUSTER_ID_KEY = "sourceClusterId";
public static final String MANAGEMENT_PORT_KEY="management.port";
public static final String MANAGEMENT_CONTEXT_PATH_KEY="management.context-path";
public static final String SERVICE_NAME_PARAM="serviceNameParam";
public static final String GROUP_NAME_PARAM="groupNameParam";
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 String NACOS_ALL_SERVICE_NAME = "ALL";
}

View File

@ -16,61 +16,34 @@
*/
package com.alibaba.nacossync.constant;
import lombok.Getter;
/**
* @author NacosSync
* @version $Id: TaskStatusEnum.java, v 0.1 2018-09-26 上午2:38 NacosSync Exp $$
*/
public enum TaskStatusEnum {
/** */
/**
* synchronization of task
*/
SYNC("SYNC", "任务同步"),
/** */
/**
* delete the task
*/
DELETE("DELETE", "任务需要被删除");
private String code;
private String desc;
@Getter
private final String code;
private final String desc;
TaskStatusEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
/**
* Getter method for property <tt>code</tt>.
*
* @return property value of code
*/
public String getCode() {
return code;
}
/**
* Setter method for property <tt>code </tt>.
*
* @param code value to be assigned to property code
*/
public void setCode(String code) {
this.code = code;
}
/**
* Getter method for property <tt>desc</tt>.
*
* @return property value of desc
*/
public String getDesc() {
return desc;
}
/**
* Setter method for property <tt>desc </tt>.
*
* @param desc value to be assigned to property desc
*/
public void setDesc(String desc) {
this.desc = desc;
}
public static boolean contains(String code) {
for (TaskStatusEnum taskStatusEnum : TaskStatusEnum.values()) {

View File

@ -16,10 +16,10 @@
*/
package com.alibaba.nacossync.dao;
import com.alibaba.nacossync.dao.repository.ClusterRepository;
import com.alibaba.nacossync.pojo.QueryCondition;
import com.alibaba.nacossync.pojo.model.ClusterDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -27,9 +27,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import com.alibaba.nacossync.dao.repository.ClusterRepository;
import com.alibaba.nacossync.pojo.model.ClusterDO;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
@ -38,15 +35,18 @@ import java.util.List;
/**
* @author NacosSync
* @version $Id: ClusterAccessService.java, v 0.1 2018-09-25 下午9:32 NacosSync Exp $$
* @version $Id: ClusterAccessService.java, v 0.1 2018-09-25 PM9:32 NacosSync Exp $$
*/
@Service
@Slf4j
public class ClusterAccessService implements PageQueryService<ClusterDO> {
@Autowired
private ClusterRepository clusterRepository;
private final ClusterRepository clusterRepository;
public ClusterAccessService(ClusterRepository clusterRepository) {
this.clusterRepository = clusterRepository;
}
public ClusterDO insert(ClusterDO clusterDO) {
return clusterRepository.save(clusterDO);
@ -103,4 +103,12 @@ public class ClusterAccessService implements PageQueryService<ClusterDO> {
predicates.add(criteriaBuilder.like(root.get("clusterName"), "%" + queryCondition.getServiceName() + "%"));
return predicates;
}
public int findClusterLevel(String sourceClusterId){
ClusterDO clusterDO = clusterRepository.findByClusterId(sourceClusterId);
if (clusterDO != null) {
return clusterDO.getClusterLevel();
}
return -1;
}
}

View File

@ -21,7 +21,7 @@ import org.springframework.data.domain.Page;
/**
* @author NacosSync
* @version $Id: PageQueryService.java, v 0.1 2018年11月05日 下午5:51 NacosSync Exp $
* @version $Id: PageQueryService.java, v 0.1 2018年11月05日 PM5:51 NacosSync Exp $
*/
public interface PageQueryService<T> {

View File

@ -14,10 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacossync.dao;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.dao.repository.TaskRepository;
import com.alibaba.nacossync.pojo.QueryCondition;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.nacossync.pojo.model.TaskDO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -25,9 +28,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import com.alibaba.nacossync.dao.repository.TaskRepository;
import com.alibaba.nacossync.pojo.model.TaskDO;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
@ -36,73 +36,98 @@ import java.util.List;
/**
* @author NacosSync
* @version $Id: TaskAccessService.java, v 0.1 2018-09-25 上午12:07 NacosSync Exp $$
* @version $Id: TaskAccessService.java, v 0.1 2018-09-25 AM12:07 NacosSync Exp $$
*/
@Service
public class TaskAccessService implements PageQueryService<TaskDO> {
@Autowired
private TaskRepository taskRepository;
private final TaskRepository taskRepository;
public TaskAccessService(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
public TaskDO findByTaskId(String taskId) {
return taskRepository.findByTaskId(taskId);
}
public void deleteTaskById(String taskId) {
taskRepository.deleteByTaskId(taskId);
}
/**
* batch delete tasks by taskIds
*
* @param taskIds
* @author yongchao9
*/
public void deleteTaskInBatch(List<String> taskIds) {
List<TaskDO> tds = taskRepository.findAllByTaskIdIn(taskIds);
taskRepository.deleteAllInBatch(tds);
}
public Iterable<TaskDO> findAll() {
return taskRepository.findAll();
}
public void addTask(TaskDO taskDO) {
taskRepository.save(taskDO);
}
public int countByDestClusterIdOrSourceClusterId(String destClusterId, String sourceClusterId) {
return taskRepository.countByDestClusterIdOrSourceClusterId(destClusterId, sourceClusterId);
}
private Predicate getPredicate(CriteriaBuilder criteriaBuilder, List<Predicate> predicates) {
Predicate[] p = new Predicate[predicates.size()];
return criteriaBuilder.and(predicates.toArray(p));
}
private List<Predicate> getPredicates(Root<TaskDO> root, CriteriaBuilder criteriaBuilder, QueryCondition queryCondition) {
private List<Predicate> getPredicates(Root<TaskDO> root, CriteriaBuilder criteriaBuilder,
QueryCondition queryCondition) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.like(root.get("serviceName"), "%" + queryCondition.getServiceName() + "%"));
return predicates;
}
@Override
public Page<TaskDO> findPageNoCriteria(Integer pageNum, Integer size) {
Pageable pageable = PageRequest.of(pageNum, size, Sort.Direction.DESC, "id");
return taskRepository.findAll(pageable);
}
@Override
public Page<TaskDO> findPageCriteria(Integer pageNum, Integer size, QueryCondition queryCondition) {
Pageable pageable = PageRequest.of(pageNum, size, Sort.Direction.DESC, "id");
return getTaskDOS(queryCondition, pageable);
}
private Page<TaskDO> getTaskDOS(QueryCondition queryCondition, Pageable pageable) {
return taskRepository.findAll(
(Specification<TaskDO>) (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = getPredicates(root,
criteriaBuilder, queryCondition);
return getPredicate(criteriaBuilder, predicates);
}, pageable);
return taskRepository.findAll((Specification<TaskDO>) (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = getPredicates(root, criteriaBuilder, queryCondition);
return getPredicate(criteriaBuilder, predicates);
}, pageable);
}
public List<TaskDO> findAllByServiceNameEqualAll() {
return taskRepository.findAllByServiceNameEqualsIgnoreCase(SkyWalkerConstants.NACOS_ALL_SERVICE_NAME);
}
public List<TaskDO> findAllByServiceNameNotEqualAll() {
return taskRepository.findAllByServiceNameNotIgnoreCase(SkyWalkerConstants.NACOS_ALL_SERVICE_NAME);
}
}

View File

@ -16,17 +16,17 @@
*/
package com.alibaba.nacossync.dao.repository;
import javax.transaction.Transactional;
import com.alibaba.nacossync.pojo.model.ClusterDO;
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.ClusterDO;
import javax.transaction.Transactional;
/**
* @author NacosSync
* @version $Id: ClusterRepository.java, v 0.1 2018-09-25 下午9:31 NacosSync Exp $$
* @version $Id: ClusterRepository.java, v 0.1 2018-09-25 PM9:31 NacosSync Exp $$
*/
public interface ClusterRepository extends CrudRepository<ClusterDO, Integer>, JpaRepository<ClusterDO, Integer>,
JpaSpecificationExecutor<ClusterDO> {
@ -34,6 +34,6 @@ public interface ClusterRepository extends CrudRepository<ClusterDO, Integer>, J
ClusterDO findByClusterId(String clusterId);
@Transactional
int deleteByClusterId(String clusterId);
void deleteByClusterId(String clusterId);
}

View File

@ -23,7 +23,7 @@ import com.alibaba.nacossync.pojo.model.SystemConfigDO;
/**
* @author NacosSync
* @version $Id: SystemConfigRepository.java, v 0.1 2018-09-25 下午9:31 NacosSync Exp $$
* @version $Id: SystemConfigRepository.java, v 0.1 2018-09-25 PM9:31 NacosSync Exp $$
*/
public interface SystemConfigRepository extends CrudRepository<SystemConfigDO, Integer>,
JpaSpecificationExecutor<SystemConfigDO> {

View File

@ -16,19 +16,17 @@
*/
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
* @version $Id: TaskRepository.java, v 0.1 2018-09-25 上午12:04 NacosSync Exp $$
* @version $Id: TaskRepository.java, v 0.1 2018-09-25 AM12:04 NacosSync Exp $$
*/
public interface TaskRepository extends CrudRepository<TaskDO, Integer>, JpaRepository<TaskDO, Integer>,
JpaSpecificationExecutor<TaskDO> {
@ -36,8 +34,15 @@ public interface TaskRepository extends CrudRepository<TaskDO, Integer>, JpaRepo
TaskDO findByTaskId(String taskId);
@Transactional
int deleteByTaskId(String taskId);
List<TaskDO> getAllByWorkerIp(String workerIp);
void deleteByTaskId(String taskId);
List<TaskDO> findAllByTaskIdIn(List<String> taskIds);
/**
* query service is alluse ns leven sync data
*/
List<TaskDO> findAllByServiceNameEqualsIgnoreCase(String serviceName);
List<TaskDO> findAllByServiceNameNotIgnoreCase(String serviceName);
int countByDestClusterIdOrSourceClusterId(String destClusterId,String sourceClusterId);
}

View File

@ -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;
}

View File

@ -22,7 +22,7 @@ import com.alibaba.nacossync.pojo.model.TaskDO;
/**
* @author NacosSync
* @version $Id: DeleteTaskEvent.java, v 0.1 2018-09-27 上午2:38 NacosSync Exp $$
* @version $Id: DeleteTaskEvent.java, v 0.1 2018-09-27 AM2:38 NacosSync Exp $$
*/
@Data
public class DeleteTaskEvent {

View File

@ -22,7 +22,7 @@ import com.alibaba.nacossync.pojo.model.TaskDO;
/**
* @author NacosSync
* @version $Id: EventModel.java, v 0.1 2018-09-27 上午1:41 NacosSync Exp $$
* @version $Id: EventModel.java, v 0.1 2018-09-27 AM1:41 NacosSync Exp $$
*/
@Data
public class SyncTaskEvent {

View File

@ -14,70 +14,83 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacossync.event.listener;
import javax.annotation.PostConstruct;
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.base.Stopwatch;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* @author NacosSync
* @version $Id: EventListener.java, v 0.1 2018-09-27 上午1:21 NacosSync Exp $$
* @version $Id: EventListener.java, v 0.1 2018-09-27 AM1:21 NacosSync Exp $$
*/
@Slf4j
@Service
public class EventListener {
@Autowired
private SyncManagerService syncManagerService;
@Autowired
private EventBus eventBus;
@Autowired
private SkyWalkerCacheServices skyWalkerCacheServices;
private final MetricsManager metricsManager;
private final SyncManagerService syncManagerService;
private final EventBus eventBus;
private final SkyWalkerCacheServices skyWalkerCacheServices;
public EventListener(MetricsManager metricsManager, SyncManagerService syncManagerService, EventBus eventBus,
SkyWalkerCacheServices skyWalkerCacheServices) {
this.metricsManager = metricsManager;
this.syncManagerService = syncManagerService;
this.eventBus = eventBus;
this.skyWalkerCacheServices = skyWalkerCacheServices;
}
@PostConstruct
public void register() {
eventBus.register(this);
}
@Subscribe
public void listenerSyncTaskEvent(SyncTaskEvent syncTaskEvent) {
public void sync(SyncTaskEvent syncTaskEvent) {
try {
syncManagerService.sync(syncTaskEvent.getTaskDO());
skyWalkerCacheServices.addFinishedTask(syncTaskEvent.getTaskDO());
Stopwatch stopwatch = Stopwatch.createStarted();
if (syncManagerService.sync(syncTaskEvent.getTaskDO(), null)) {
skyWalkerCacheServices.addFinishedTask(syncTaskEvent.getTaskDO());
metricsManager.record(MetricsStatisticsType.SYNC_TASK_RT, stopwatch.elapsed().toMillis());
} else {
log.warn("syncTaskEvent process error");
}
} catch (Exception e) {
log.warn("listenerSyncTaskEvent process error", e);
log.warn("syncTaskEvent process error", e);
}
}
@Subscribe
public void listenerDeleteTaskEvent(DeleteTaskEvent deleteTaskEvent) {
public void delete(DeleteTaskEvent deleteTaskEvent) {
try {
syncManagerService.delete(deleteTaskEvent.getTaskDO());
skyWalkerCacheServices.addFinishedTask(deleteTaskEvent.getTaskDO());
Stopwatch stopwatch = Stopwatch.createStarted();
if (syncManagerService.delete(deleteTaskEvent.getTaskDO())) {
skyWalkerCacheServices.removeFinishedTask(deleteTaskEvent.getTaskDO().getOperationId());
metricsManager.record(MetricsStatisticsType.DELETE_TASK_RT, stopwatch.elapsed().toMillis());
} else {
log.warn("deleteTaskEvent delete failure");
}
} catch (Exception e) {
log.warn("listenerDeleteTaskEvent process error", e);
log.warn("deleteTaskEvent delete failure.", e);
}
}
}

View File

@ -20,7 +20,7 @@ import com.alibaba.nacossync.constant.ResultCodeEnum;
/**
* @author NacosSync
* @version $Id: SkyWalkerException.java, v 0.1 2018-09-30 上午10:09 NacosSync Exp $$
* @version $Id: SkyWalkerException.java, v 0.1 2018-09-30 AM10:09 NacosSync Exp $$
*/
public class SkyWalkerException extends RuntimeException {
/**

View File

@ -1,16 +0,0 @@
package com.alibaba.nacossync.extension;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* @author yangyshdan
* @version $Id: ConfigServerSyncManagerService.java, v 0.1 2018-11-12 下午5:17 NacosSync Exp $$
*/
@Slf4j
@Service
public class NacosSyncService {
}

View File

@ -1,63 +1,88 @@
/*
* 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
* 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
* 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.
* 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.extension;
import java.util.Hashtable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
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 com.alibaba.nacossync.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
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 下午5:17 NacosSync Exp $$
* @version $Id: SyncManagerService.java, v 0.1 2018-09-25 PM5:17 NacosSync Exp $$
*/
@Slf4j
@Service
public class SyncManagerService {
public class SyncManagerService implements InitializingBean, ApplicationContextAware {
@Autowired
protected SkyWalkerCacheServices skyWalkerCacheServices;
protected final SkyWalkerCacheServices skyWalkerCacheServices;
private Hashtable<ClusterTypeEnum, SyncService> syncServiceMap = new Hashtable<ClusterTypeEnum, SyncService>();
private final ConcurrentHashMap<String, SyncService> syncServiceMap = new ConcurrentHashMap<String, SyncService>();
public void register(ClusterTypeEnum type, SyncService service) {
syncServiceMap.put(type, service);
private ApplicationContext applicationContext;
public SyncManagerService(
SkyWalkerCacheServices skyWalkerCacheServices) {
this.skyWalkerCacheServices = skyWalkerCacheServices;
}
public boolean delete(TaskDO taskDO) throws NacosException {
ClusterTypeEnum type = this.skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId());
return syncServiceMap.get(type).delete(taskDO);
return getSyncService(taskDO.getSourceClusterId(), taskDO.getDestClusterId()).delete(taskDO);
}
public boolean sync(TaskDO taskDO) {
public boolean sync(TaskDO taskDO, Integer index) {
ClusterTypeEnum type = this.skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId());
return getSyncService(taskDO.getSourceClusterId(), taskDO.getDestClusterId()).sync(taskDO, index);
return syncServiceMap.get(type).sync(taskDO);
}
@Override
public void afterPropertiesSet() {
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), (SyncService) value);
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
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);
return syncServiceMap.get(generateSyncKey(sourceClusterType, destClusterType));
}
}

View File

@ -1,11 +1,61 @@
/*
* 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.extension;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.pojo.model.TaskDO;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
/**
* @author NacosSync
* @version $Id: SyncManagerService.java, v 0.1 2018-09-25 下午5:17 NacosSync Exp $$
*/
public interface SyncService {
public boolean delete(TaskDO taskDO);
/**
* delete the sync task
*
* @param taskDO
* @return
*/
boolean delete(TaskDO taskDO);
/**
*
* @param taskDO
* @param index
* @return
*/
boolean sync(TaskDO taskDO, Integer index);
public boolean sync(TaskDO taskDO);
/**
* Determines that the current instance data is from another source cluster
*/
default boolean needSync(Map<String, String> sourceMetaData) {
boolean syncTag = StringUtils.isBlank(sourceMetaData.get(SkyWalkerConstants.SYNC_INSTANCE_TAG));
boolean blank = StringUtils.isBlank(sourceMetaData.get(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY));
return syncTag && blank;
}
/**
* Determines whether the source cluster ID of the current instance is the same as the source
* cluster ID of the task
*/
default boolean needDelete(Map<String, String> destMetaData, TaskDO taskDO) {
return StringUtils.equals(destMetaData.get(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY),
taskDO.getSourceClusterId());
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.extension.annotation;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.extension.SyncManagerService;
import com.alibaba.nacossync.extension.SyncService;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* Mainly used to mark the SyncService implementation
* of which source cluster to which destination cluster
*
* @author paderlol
* @date 2018-12-31 15:28
* @see SyncService
* @see SyncManagerService
*
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface NacosSyncService {
@AliasFor(annotation = Component.class)
String value() default "";
ClusterTypeEnum sourceCluster();
ClusterTypeEnum destinationCluster();
}

View File

@ -0,0 +1,81 @@
/*
* 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.extension.eureka;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author liu jun jie
* @date 2019-06-26
*/
@Slf4j
public class EurekaBeatReactor {
private final ScheduledExecutorService executorService;
private static final long CLIENT_BEAT_INTERVAL = 5 * 1000;
private final Map<String, InstanceInfo> eurekaBeat = new ConcurrentHashMap<>();
private final EurekaHttpClient eurekaHttpClient;
public EurekaBeatReactor(EurekaHttpClient eurekaHttpClient) {
this.eurekaHttpClient = eurekaHttpClient;
executorService = new ScheduledThreadPoolExecutor(30, r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName("com.alibaba.nacossync.eureka.beat.sender");
return thread;
});
executorService.schedule(new BeatProcessor(), 0, TimeUnit.SECONDS);
}
public void addInstance(String key, InstanceInfo value) {
this.eurekaBeat.put(key, value);
}
public void removeInstance(String key) {
log.debug("[BEAT] removing beat: {} to beat map.", key);
this.eurekaBeat.remove(key);
}
class BeatProcessor implements Runnable {
@Override
public void run() {
try {
for (Map.Entry<String, InstanceInfo> entry : eurekaBeat.entrySet()) {
InstanceInfo instanceInfo = entry.getValue();
executorService.schedule(() -> {
log.debug("[BEAT] adding beat: {} to beat map.", instanceInfo.getId());
eurekaHttpClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, InstanceInfo.InstanceStatus.UP);
}, 0, TimeUnit.MILLISECONDS);
}
} catch (Exception e) {
log.error("[CLIENT-BEAT] Exception while scheduling beat.", e);
} finally {
executorService.schedule(this, CLIENT_BEAT_INTERVAL, TimeUnit.MILLISECONDS);
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.extension.eureka;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import org.springframework.http.HttpStatus;
import java.util.List;
import java.util.Objects;
/**
* @author liu jun jie
* @date 2019-06-26
*/
public class EurekaNamingService {
private final EurekaHttpClient eurekaHttpClient;
private final EurekaBeatReactor beatReactor;
public EurekaNamingService(EurekaHttpClient eurekaHttpClient) {
this.eurekaHttpClient = eurekaHttpClient;
beatReactor = new EurekaBeatReactor(eurekaHttpClient);
}
public void registerInstance(InstanceInfo instanceInfo) {
EurekaHttpResponse<Void> response = eurekaHttpClient.register(instanceInfo);
if (Objects.requireNonNull(HttpStatus.resolve(response.getStatusCode())).is2xxSuccessful()) {
beatReactor.addInstance(instanceInfo.getId(), instanceInfo);
}
}
public void deregisterInstance(InstanceInfo instanceInfo) {
EurekaHttpResponse<Void> response = eurekaHttpClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
if (Objects.requireNonNull(HttpStatus.resolve(response.getStatusCode())).is2xxSuccessful()) {
beatReactor.removeInstance(instanceInfo.getId());
}
}
public List<InstanceInfo> getApplications(String serviceName) {
EurekaHttpResponse<Application> eurekaHttpResponse =
eurekaHttpClient.getApplication(serviceName);
if (Objects.requireNonNull(HttpStatus.resolve(eurekaHttpResponse.getStatusCode())).is2xxSuccessful()) {
return eurekaHttpResponse.getEntity().getInstances();
}
return null;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.extension.event;
import com.alibaba.nacossync.pojo.model.TaskDO;
import lombok.Data;
import java.util.function.Consumer;
/**
* @author paderlol
* @date: 2019-01-12 22:28
*/
@Data
public class SpecialSyncEvent {
private TaskDO taskDO;
private Consumer<TaskDO> syncAction;
}

View File

@ -0,0 +1,44 @@
/*
* 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.extension.event;
import com.alibaba.nacossync.pojo.model.TaskDO;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* @author paderlol
* @date 2019-01-12 22:42
*/
@Service
public class SpecialSyncEventBus {
private final ConcurrentHashMap<String, SpecialSyncEvent> specialSyncEventRegistry = new ConcurrentHashMap<>();
public void subscribe(TaskDO taskDO, Consumer<TaskDO> syncAction) {
SpecialSyncEvent specialSyncEvent = new SpecialSyncEvent();
specialSyncEvent.setTaskDO(taskDO);
specialSyncEvent.setSyncAction(syncAction);
specialSyncEventRegistry.putIfAbsent(taskDO.getTaskId(), specialSyncEvent);
}
public void unsubscribe(TaskDO taskDO) {
specialSyncEventRegistry.remove(taskDO.getTaskId());
}
public Collection<SpecialSyncEvent> getAllSpecialSyncEvent() {
return specialSyncEventRegistry.values();
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.extension.event.listener;
import com.alibaba.nacossync.extension.event.SpecialSyncEvent;
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.function.Consumer;
/**
* @author paderlol
* @date: 2019-01-12 22:29
*/
@Service
@Slf4j
public class SpecialSyncEventListener {
private final EventBus eventBus;
public SpecialSyncEventListener(EventBus eventBus) {
this.eventBus = eventBus;
}
@PostConstruct
public void init() {
eventBus.register(this);
}
@Subscribe
public void listenerSpecialSyncEvent(SpecialSyncEvent specialSyncEvent) {
TaskDO taskDO = specialSyncEvent.getTaskDO();
Consumer<TaskDO> syncAction = specialSyncEvent.getSyncAction();
syncAction.accept(taskDO);
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.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;
/**
* @author paderlol
* @date 2018-12-24 22:08
*/
@Slf4j
public abstract class AbstractServerHolderImpl<T> implements Holder<T> {
private final Map<String, T> serviceMap = new ConcurrentHashMap<>();
@Autowired
protected SkyWalkerCacheServices skyWalkerCacheServices;
@Override
public T get(String clusterId) {
return serviceMap.computeIfAbsent(clusterId, clusterKey -> {
try {
log.info("Starting create cluster server, clusterId={}", clusterId);
return createServer(clusterId, () -> skyWalkerCacheServices.getClusterConnectKey(clusterId));
} catch (Exception e) {
log.error(String.format("clusterId=%s, start server failed", clusterId), e);
return null;
}
});
}
/**
* Create real cluster client instance
*
* @param clusterId cluster id
* @param serverAddressSupplier server address
* @return cluster client instance
*/
abstract T createServer(String clusterId, Supplier<String> serverAddressSupplier)
throws Exception;
}

View File

@ -0,0 +1,40 @@
/*
* 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.extension.holder;
import com.ecwid.consul.v1.ConsulClient;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.net.URL;
/**
* @author paderlol
* @date: 2018-12-31 16:26
*/
@Service
@Slf4j
public class ConsulServerHolder extends AbstractServerHolderImpl<ConsulClient> {
public static final String HTTP = "http://";
@Override
ConsulClient createServer(String clusterId, Supplier<String> serverAddressSupplier) throws Exception {
String serverAddress = serverAddressSupplier.get();
serverAddress = serverAddress.startsWith(HTTP) ? serverAddress : HTTP + serverAddress;
URL url = new URL(serverAddress);
return new ConsulClient(url.getHost(), url.getPort());
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.extension.holder;
import com.alibaba.nacossync.extension.eureka.EurekaNamingService;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
import org.springframework.stereotype.Service;
import java.util.function.Supplier;
/**
* @author paderlol
* @date: 2018-12-31 16:26
*/
@Service
@Slf4j
public class EurekaServerHolder extends AbstractServerHolderImpl<EurekaNamingService> {
private static final String HTTP_PREFIX = "http://";
private static final String HTTPS_PREFIX = "https://";
@Override
EurekaNamingService createServer(String clusterId, Supplier<String> serverAddressSupplier) throws Exception {
RestTemplateTransportClientFactory restTemplateTransportClientFactory = new RestTemplateTransportClientFactory();
EurekaEndpoint eurekaEndpoint = new DefaultEndpoint(addHttpPrefix(serverAddressSupplier.get()));
EurekaHttpClient eurekaHttpClient = restTemplateTransportClientFactory.newClient(eurekaEndpoint);
return new EurekaNamingService(eurekaHttpClient);
}
public String addHttpPrefix(String input) {
if (input == null || input.isEmpty()) {
return input;
}
if (!input.startsWith(HTTP_PREFIX) && !input.startsWith(HTTPS_PREFIX)) {
input = HTTP_PREFIX + input;
}
return input;
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.extension.holder;
/**
* Cluster client service
* @author paderlol
* @date 2018-12-24 21:59
*/
public interface Holder<T> {
/**
* Through the cluster ID and namespace fetch cluster client service
* @param clusterId cluster id
* @return
* @throws Exception
*/
T get(String clusterId) throws Exception;
}

View File

@ -0,0 +1,65 @@
/*
* 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.extension.holder;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacossync.dao.ClusterAccessService;
import com.alibaba.nacossync.pojo.model.ClusterDO;
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.function.Supplier;
/**
* @author paderlol
* @date: 2018-12-24 21:48
*/
@Service
@Slf4j
public class NacosServerHolder extends AbstractServerHolderImpl<NamingService> {
private final ClusterAccessService clusterAccessService;
public NacosServerHolder(ClusterAccessService clusterAccessService) {
this.clusterAccessService = clusterAccessService;
}
@Override
NamingService createServer(String clusterId, Supplier<String> serverAddressSupplier)
throws Exception {
List<String> allClusterConnectKey = skyWalkerCacheServices
.getAllClusterConnectKey(clusterId);
ClusterDO clusterDO = clusterAccessService.findByClusterId(clusterId);
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)
);
return NamingFactory.createNamingService(properties);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.extension.holder;
import com.google.common.base.Joiner;
import java.util.List;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.retry.RetryNTimes;
import org.springframework.stereotype.Service;
/**
* @author paderlol
* @date: 2018-12-24 22:07
*/
@Service
@Slf4j
public class ZookeeperServerHolder extends AbstractServerHolderImpl<CuratorFramework> {
@Override
CuratorFramework createServer(String clusterId, Supplier<String> serverAddressSupplier) {
List<String> allClusterConnectKey = skyWalkerCacheServices
.getAllClusterConnectKey(clusterId);
String serverList = Joiner.on(",").join(allClusterConnectKey);
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
.connectString(serverList)
.retryPolicy(new RetryNTimes(1, 3000))
.connectionTimeoutMs(5000);
CuratorFramework client = builder.build();
client.getConnectionStateListenable().addListener((clientInstance, state) -> {
if (state == ConnectionState.LOST) {
log.error("zk address: {} client state LOST",serverList);
} else if (state == ConnectionState.CONNECTED) {
log.info("zk address: {} client state CONNECTED",serverList);
} else if (state == ConnectionState.RECONNECTED) {
log.info("zk address: {} client state RECONNECTED",serverList);
}
});
client.start();
return client;
}
}

View File

@ -0,0 +1,265 @@
package com.alibaba.nacossync.extension.impl;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.extension.SyncService;
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;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault;
@Slf4j
public abstract class AbstractNacosSync implements SyncService {
private final Map<String, EventListener> listenerMap = new ConcurrentHashMap<>();
private final Map<String, Set<String>> sourceInstanceSnapshot = new ConcurrentHashMap<>();
private final Map<String, Integer> syncTaskTap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, TaskDO> allSyncTaskMap = new ConcurrentHashMap<>();
private ScheduledExecutorService executorService;
@Autowired
private MetricsManager metricsManager;
@Getter
@Autowired
private NacosServerHolder nacosServerHolder;
/**
* 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 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;
}
Collection<TaskDO> taskCollections = allSyncTaskMap.values();
List<TaskDO> 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(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("Unexpected error during sync task, taskId:{}", taskId, e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
@Override
public boolean sync(TaskDO taskDO, Integer index) {
String taskId = taskDO.getTaskId();
try {
NamingService sourceNamingService = nacosServerHolder.get(taskDO.getSourceClusterId());
allSyncTaskMap.put(taskId, taskDO);
//防止暂停同步任务后,重新同步/或删除任务以后新建任务不会再接收到新的事件导致不能同步,所以每次订阅事件之前,先全量同步一次任务
doSync(taskId, taskDO, sourceNamingService);
this.listenerMap.putIfAbsent(taskId, event -> {
if (event instanceof NamingEvent) {
try {
doSync(taskId, taskDO, sourceNamingService);
} catch (Exception e) {
log.error("event process fail, taskId:{}", taskId, e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
}
}
});
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("Unexpected error during sync task, taskId:{}", taskId, e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
private void doSync(String taskId, TaskDO taskDO, NamingService sourceNamingService) throws Exception {
if (syncTaskTap.putIfAbsent(taskId, 1) != null) {
log.info("任务Id:{}上一个同步任务尚未结束", taskId);
return;
}
try {
// 直接从本地保存的serviceInfoMap中取订阅的服务实例
List<Instance> sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(),
getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
// 先删除不存在的
this.removeInvalidInstance(taskDO, sourceInstances);
// 同步实例
this.syncNewInstance(taskDO, sourceInstances);
} finally {
syncTaskTap.remove(taskId);
}
}
private void syncNewInstance(TaskDO taskDO, List<Instance> sourceInstances) throws NacosException {
Set<String> latestSyncInstance = new TreeSet<>();
//再次添加新实例
String taskId = taskDO.getTaskId();
Set<String> instanceKeys = sourceInstanceSnapshot.get(taskId);
for (Instance instance : sourceInstances) {
if (needSync(instance.getMetadata())) {
String instanceKey = composeInstanceKey(instance.getIp(), instance.getPort());
if (CollectionUtils.isEmpty(instanceKeys) || !instanceKeys.contains(instanceKey)) {
register(taskDO, instance);
}
latestSyncInstance.add(instanceKey);
}
}
if (CollectionUtils.isNotEmpty(latestSyncInstance)) {
log.info("任务Id:{},已同步实例个数:{}", taskId, latestSyncInstance.size());
sourceInstanceSnapshot.put(taskId, latestSyncInstance);
} else {
// latestSyncInstance为空表示源集群中需要同步的所有实例即非nacos-sync同步过来的实例已经下线,清除本地持有快照
sourceInstanceSnapshot.remove(taskId);
}
}
private void removeInvalidInstance(TaskDO taskDO, List<Instance> sourceInstances) throws Exception {
String taskId = taskDO.getTaskId();
if (this.sourceInstanceSnapshot.containsKey(taskId)) {
Set<String> oldInstanceKeys = this.sourceInstanceSnapshot.get(taskId);
Set<String> newInstanceKeys = sourceInstances.stream()
.map(instance -> composeInstanceKey(instance.getIp(), instance.getPort()))
.collect(Collectors.toSet());
oldInstanceKeys.removeAll(newInstanceKeys);
if (CollectionUtils.isNotEmpty(oldInstanceKeys)) {
log.info("任务Id:{},移除无效同步实例:{}", taskId, oldInstanceKeys);
removeInvalidInstance(taskDO, oldInstanceKeys);
}
}
}
@Override
public boolean needDelete(Map<String, String> destMetaData, TaskDO taskDO) {
return SyncService.super.needDelete(destMetaData, taskDO);
}
@Override
public boolean needSync(Map<String, String> sourceMetaData) {
return SyncService.super.needSync(sourceMetaData);
}
public abstract String composeInstanceKey(String ip, int port);
public abstract void register(TaskDO taskDO, Instance instance);
public abstract void deregisterInstance(TaskDO taskDO) throws Exception;
public abstract void removeInvalidInstance(TaskDO taskDO, Set<String> invalidInstanceKeys) throws Exception;
}

View File

@ -0,0 +1,161 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.SyncService;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.event.SpecialSyncEventBus;
import com.alibaba.nacossync.extension.holder.ConsulServerHolder;
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.ConsulUtils;
import com.alibaba.nacossync.util.NacosUtils;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.health.model.HealthService;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Consul 同步 Nacos
*
* @author paderlol
* @date: 2018-12-31 16:25
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.CONSUL, destinationCluster = ClusterTypeEnum.NACOS)
public class ConsulSyncToNacosServiceImpl implements SyncService {
private final MetricsManager metricsManager;
private final ConsulServerHolder consulServerHolder;
private final SkyWalkerCacheServices skyWalkerCacheServices;
private final NacosServerHolder nacosServerHolder;
private final SpecialSyncEventBus specialSyncEventBus;
public ConsulSyncToNacosServiceImpl(ConsulServerHolder consulServerHolder,
SkyWalkerCacheServices skyWalkerCacheServices, NacosServerHolder nacosServerHolder,
SpecialSyncEventBus specialSyncEventBus, MetricsManager metricsManager) {
this.consulServerHolder = consulServerHolder;
this.skyWalkerCacheServices = skyWalkerCacheServices;
this.nacosServerHolder = nacosServerHolder;
this.specialSyncEventBus = specialSyncEventBus;
this.metricsManager = metricsManager;
}
@Override
public boolean delete(TaskDO taskDO) {
try {
specialSyncEventBus.unsubscribe(taskDO);
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
List<Instance> allInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()));
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)) {
destNamingService.deregisterInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
}
}
} catch (Exception e) {
log.error("delete task from consul to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR);
return false;
}
return true;
}
@Override
public boolean sync(TaskDO taskDO, Integer index) {
try {
ConsulClient consulClient = consulServerHolder.get(taskDO.getSourceClusterId());
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
Response<List<HealthService>> response =
consulClient.getHealthServices(taskDO.getServiceName(), true, QueryParams.DEFAULT);
List<HealthService> healthServiceList = response.getValue();
Set<String> instanceKeys = new HashSet<>();
overrideAllInstance(taskDO, destNamingService, healthServiceList, instanceKeys);
cleanAllOldInstance(taskDO, destNamingService, instanceKeys);
specialSyncEventBus.subscribe(taskDO, t->sync(t, index));
} catch (Exception e) {
log.error("Sync task from consul to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
private void cleanAllOldInstance(TaskDO taskDO, NamingService destNamingService, Set<String> instanceKeys)
throws NacosException {
List<Instance> allInstances = destNamingService.getAllInstances(taskDO.getServiceName());
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)
&& !instanceKeys.contains(composeInstanceKey(instance.getIp(), instance.getPort()))) {
destNamingService.deregisterInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
}
}
}
private void overrideAllInstance(TaskDO taskDO, NamingService destNamingService,
List<HealthService> healthServiceList, Set<String> instanceKeys) throws NacosException {
for (HealthService healthService : healthServiceList) {
if (needSync(ConsulUtils.transferMetadata(healthService.getService().getTags()))) {
destNamingService.registerInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()),
buildSyncInstance(healthService, taskDO));
instanceKeys.add(composeInstanceKey(healthService.getService().getAddress(),
healthService.getService().getPort()));
}
}
}
private Instance buildSyncInstance(HealthService instance, TaskDO taskDO) {
Instance temp = new Instance();
temp.setIp(instance.getService().getAddress());
temp.setPort(instance.getService().getPort());
Map<String, String> metaData = new HashMap<>(ConsulUtils.transferMetadata(instance.getService().getTags()));
metaData.put(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
metaData.put(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
temp.setMetadata(metaData);
return temp;
}
private String composeInstanceKey(String ip, int port) {
return ip + ":" + port;
}
}

View File

@ -0,0 +1,194 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.SyncService;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.eureka.EurekaNamingService;
import com.alibaba.nacossync.extension.event.SpecialSyncEventBus;
import com.alibaba.nacossync.extension.holder.EurekaServerHolder;
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.NacosUtils;
import com.netflix.appinfo.InstanceInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* eureka
*
* @author paderlol
* @date: 2018-12-31 16:25
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.EUREKA, destinationCluster = ClusterTypeEnum.NACOS)
public class EurekaSyncToNacosServiceImpl implements SyncService {
private final MetricsManager metricsManager;
private final EurekaServerHolder eurekaServerHolder;
private final SkyWalkerCacheServices skyWalkerCacheServices;
private final NacosServerHolder nacosServerHolder;
private final SpecialSyncEventBus specialSyncEventBus;
@Autowired
public EurekaSyncToNacosServiceImpl(EurekaServerHolder eurekaServerHolder,
SkyWalkerCacheServices skyWalkerCacheServices, NacosServerHolder nacosServerHolder,
SpecialSyncEventBus specialSyncEventBus, MetricsManager metricsManager) {
this.eurekaServerHolder = eurekaServerHolder;
this.skyWalkerCacheServices = skyWalkerCacheServices;
this.nacosServerHolder = nacosServerHolder;
this.specialSyncEventBus = specialSyncEventBus;
this.metricsManager = metricsManager;
}
@Override
public boolean delete(TaskDO taskDO) {
try {
specialSyncEventBus.unsubscribe(taskDO);
EurekaNamingService eurekaNamingService = eurekaServerHolder.get(taskDO.getSourceClusterId());
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
List<InstanceInfo> eurekaInstances = eurekaNamingService.getApplications(taskDO.getServiceName());
deleteAllInstanceFromEureka(taskDO, destNamingService, eurekaInstances);
} catch (Exception e) {
log.error("delete a task from eureka to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR);
return false;
}
return true;
}
@Override
public boolean sync(TaskDO taskDO,Integer index) {
try {
EurekaNamingService eurekaNamingService = eurekaServerHolder.get(taskDO.getSourceClusterId());
NamingService destNamingService = nacosServerHolder.get(taskDO.getDestClusterId());
List<InstanceInfo> eurekaInstances = eurekaNamingService.getApplications(taskDO.getServiceName());
List<Instance> nacosInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()));
if (CollectionUtils.isEmpty(eurekaInstances)) {
// Clear all instance from Nacos
deleteAllInstance(taskDO, destNamingService, nacosInstances);
} else {
if (!CollectionUtils.isEmpty(nacosInstances)) {
// Remove invalid instance from Nacos
removeInvalidInstance(taskDO, destNamingService, eurekaInstances, nacosInstances);
}
addValidInstance(taskDO, destNamingService, eurekaInstances);
}
specialSyncEventBus.subscribe(taskDO, t->sync(t, index));
} catch (Exception e) {
log.error("sync task from eureka to nacos was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
private void addValidInstance(TaskDO taskDO, NamingService destNamingService, List<InstanceInfo> eurekaInstances)
throws NacosException {
for (InstanceInfo instance : eurekaInstances) {
if (needSync(instance.getMetadata())) {
log.info("Add service instance from Eureka, serviceName={}, Ip={}, port={}",
instance.getAppName(), instance.getIPAddr(), instance.getPort());
destNamingService.registerInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), buildSyncInstance(instance,
taskDO));
}
}
}
private void deleteAllInstanceFromEureka(TaskDO taskDO, NamingService destNamingService,
List<InstanceInfo> eurekaInstances)
throws NacosException {
if (CollectionUtils.isEmpty(eurekaInstances)) {
return;
}
for (InstanceInfo instance : eurekaInstances) {
if (needSync(instance.getMetadata())) {
log.info("Delete service instance from Eureka, serviceName={}, Ip={}, port={}",
instance.getAppName(), instance.getIPAddr(), instance.getPort());
destNamingService.deregisterInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), buildSyncInstance(instance, taskDO));
}
}
}
private void removeInvalidInstance(TaskDO taskDO, NamingService destNamingService,
List<InstanceInfo> eurekaInstances, List<Instance> nacosInstances) throws NacosException {
for (Instance instance : nacosInstances) {
if (!isExistInEurekaInstance(eurekaInstances, instance) && needDelete(instance.getMetadata(), taskDO)) {
log.info("Remove invalid service instance from Nacos, serviceName={}, Ip={}, port={}",
instance.getServiceName(), instance.getIp(), instance.getPort());
destNamingService.deregisterInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance.getIp(), instance.getPort());
}
}
}
private boolean isExistInEurekaInstance(List<InstanceInfo> eurekaInstances, Instance nacosInstance) {
return eurekaInstances.stream().anyMatch(instance -> instance.getIPAddr().equals(nacosInstance.getIp())
&& instance.getPort() == nacosInstance.getPort());
}
private void deleteAllInstance(TaskDO taskDO, NamingService destNamingService, List<Instance> allInstances)
throws NacosException {
for (Instance instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)) {
destNamingService.deregisterInstance(taskDO.getServiceName(),
NacosUtils.getGroupNameOrDefault(taskDO.getGroupName()), instance);
}
}
}
private Instance buildSyncInstance(InstanceInfo instance, TaskDO taskDO) {
Instance temp = new Instance();
temp.setIp(instance.getIPAddr());
temp.setPort(instance.getPort());
temp.setServiceName(instance.getAppName());
temp.setHealthy(true);
Map<String, String> metaData = new HashMap<>(instance.getMetadata());
metaData.put(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
metaData.put(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
temp.setMetadata(metaData);
return temp;
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.holder.ConsulServerHolder;
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.alibaba.nacossync.util.ConsulUtils;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.health.model.HealthService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author zhanglong
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.CONSUL)
public class NacosSyncToConsulServiceImpl extends AbstractNacosSync {
private static final String DELIMITER = "=";
private final SkyWalkerCacheServices skyWalkerCacheServices;
private final ConsulServerHolder consulServerHolder;
public NacosSyncToConsulServiceImpl(SkyWalkerCacheServices skyWalkerCacheServices,
ConsulServerHolder consulServerHolder) {
this.skyWalkerCacheServices = skyWalkerCacheServices;
this.consulServerHolder = consulServerHolder;
}
@Override
public String composeInstanceKey(String ip, int port) {
return String.join(":", ip, String.valueOf(port));
}
@Override
public void register(TaskDO taskDO, Instance instance) {
ConsulClient consulClient = consulServerHolder.get(taskDO.getDestClusterId());
consulClient.agentServiceRegister(buildSyncInstance(instance, taskDO));
}
@Override
public void deregisterInstance(TaskDO taskDO) throws Exception {
ConsulClient consulClient = consulServerHolder.get(taskDO.getDestClusterId());
Response<List<HealthService>> serviceResponse = consulClient.getHealthServices(taskDO.getServiceName(), true,
QueryParams.DEFAULT);
List<HealthService> healthServices = serviceResponse.getValue();
for (HealthService healthService : healthServices) {
if (needDelete(ConsulUtils.transferMetadata(healthService.getService().getTags()), taskDO)) {
consulClient.agentServiceDeregister(
URLEncoder.encode(healthService.getService().getId(), StandardCharsets.UTF_8));
}
}
}
@Override
public void removeInvalidInstance(TaskDO taskDO, Set<String> invalidInstanceKeys)
throws UnsupportedEncodingException {
ConsulClient consulClient = consulServerHolder.get(taskDO.getDestClusterId());
Response<List<HealthService>> serviceResponse = consulClient.getHealthServices(taskDO.getServiceName(), true,
QueryParams.DEFAULT);
List<HealthService> healthServices = serviceResponse.getValue();
for (HealthService healthService : healthServices) {
if (needDelete(ConsulUtils.transferMetadata(healthService.getService().getTags()), taskDO)
&& !invalidInstanceKeys.contains(composeInstanceKey(healthService.getService().getAddress(),
healthService.getService().getPort()))) {
consulClient.agentServiceDeregister(
URLEncoder.encode(healthService.getService().getId(), StandardCharsets.UTF_8));
}
}
}
public NewService buildSyncInstance(Instance instance, TaskDO taskDO) {
NewService newService = new NewService();
newService.setAddress(instance.getIp());
newService.setPort(instance.getPort());
newService.setName(taskDO.getServiceName());
newService.setId(instance.getInstanceId());
List<String> tags = Lists.newArrayList();
tags.addAll(instance.getMetadata().entrySet().stream()
.map(entry -> String.join(DELIMITER, entry.getKey(), entry.getValue())).collect(Collectors.toList()));
tags.add(String.join(DELIMITER, SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId()));
tags.add(String.join(DELIMITER, SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode()));
tags.add(String.join(DELIMITER, SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId()));
newService.setTags(tags);
return newService;
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.eureka.EurekaNamingService;
import com.alibaba.nacossync.extension.holder.EurekaServerHolder;
import com.alibaba.nacossync.pojo.model.TaskDO;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.LeaseInfo;
import com.netflix.appinfo.MyDataCenterInfo;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author zhanglong
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.EUREKA)
public class NacosSyncToEurekaServiceImpl extends AbstractNacosSync {
private final EurekaServerHolder eurekaServerHolder;
private final SkyWalkerCacheServices skyWalkerCacheServices;
public NacosSyncToEurekaServiceImpl(EurekaServerHolder eurekaServerHolder,
SkyWalkerCacheServices skyWalkerCacheServices) {
this.eurekaServerHolder = eurekaServerHolder;
this.skyWalkerCacheServices = skyWalkerCacheServices;
}
@Override
public String composeInstanceKey(String ip, int port) {
return ip + ":" + port;
}
@Override
public void register(TaskDO taskDO, Instance instance) {
EurekaNamingService destNamingService = eurekaServerHolder.get(taskDO.getDestClusterId());
destNamingService.registerInstance(buildSyncInstance(instance, taskDO));
}
@Override
public void deregisterInstance(TaskDO taskDO) {
EurekaNamingService destNamingService = eurekaServerHolder.get(taskDO.getDestClusterId());
List<InstanceInfo> allInstances = destNamingService.getApplications(taskDO.getServiceName());
if (allInstances != null) {
for (InstanceInfo instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO)) {
destNamingService.deregisterInstance(instance);
}
}
}
}
@Override
public void removeInvalidInstance(TaskDO taskDO, Set<String> invalidInstanceKeys) {
EurekaNamingService destNamingService = eurekaServerHolder.get(taskDO.getDestClusterId());
List<InstanceInfo> allInstances = destNamingService.getApplications(taskDO.getServiceName());
if (CollectionUtils.isNotEmpty(allInstances)) {
for (InstanceInfo instance : allInstances) {
if (needDelete(instance.getMetadata(), taskDO) && invalidInstanceKeys.contains(
composeInstanceKey(instance.getIPAddr(), instance.getPort()))) {
destNamingService.deregisterInstance(instance);
}
}
}
}
private InstanceInfo buildSyncInstance(Instance instance, TaskDO taskDO) {
DataCenterInfo dataCenterInfo = new MyDataCenterInfo(DataCenterInfo.Name.MyOwn);
final Map<String, String> instanceMetadata = instance.getMetadata();
HashMap<String, String> metadata = new HashMap<>(16);
metadata.put(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
metadata.put(SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
metadata.put(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
metadata.putAll(instanceMetadata);
String homePageUrl = obtainHomePageUrl(instance, instanceMetadata);
String serviceName = taskDO.getServiceName();
return new InstanceInfo(instance.getIp() + ":" + serviceName + ":" + instance.getPort(), serviceName, null,
instance.getIp(), null, new InstanceInfo.PortWrapper(true, instance.getPort()), null,
homePageUrl + "/actuator/env", homePageUrl + "/actuator/info", homePageUrl + "/actuator/health", null,
serviceName, serviceName, 1, dataCenterInfo, instance.getIp(), InstanceInfo.InstanceStatus.UP,
InstanceInfo.InstanceStatus.UNKNOWN, null, new LeaseInfo(30, 90, 0L, 0L, 0L, 0L, 0L), false, metadata,
System.currentTimeMillis(), System.currentTimeMillis(), null, null);
}
private String obtainHomePageUrl(Instance instance, Map<String, String> instanceMetadata) {
final String managementContextPath = obtainManagementContextPath(instanceMetadata);
final String managementPort = instanceMetadata.getOrDefault(SkyWalkerConstants.MANAGEMENT_PORT_KEY,
String.valueOf(instance.getPort()));
return String.format("http://%s:%s%s", instance.getIp(), managementPort, managementContextPath);
}
private String obtainManagementContextPath(Map<String, String> instanceMetadata) {
final String path = instanceMetadata.getOrDefault(SkyWalkerConstants.MANAGEMENT_CONTEXT_PATH_KEY, "");
if (path.endsWith("/")) {
return path.substring(0, path.length() - 1);
}
return path;
}
}

View File

@ -0,0 +1,443 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.dao.ClusterAccessService;
import com.alibaba.nacossync.extension.SyncService;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
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.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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.alibaba.nacossync.constant.SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY;
import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault;
/**
* @author yangyshdan
* @version $Id: ConfigServerSyncManagerService.java, v 0.1 2018-11-12 下午5:17 NacosSync Exp $$
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.NACOS)
public class NacosSyncToNacosServiceImpl implements SyncService, InitializingBean, DisposableBean {
private final Map<String, EventListener> listenerMap = new ConcurrentHashMap<>();
private final Map<String, Integer> syncTaskTap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, TaskDO> allSyncTaskMap = new ConcurrentHashMap<>();
private ScheduledExecutorService executorService;
private final MetricsManager metricsManager;
private final NacosServerHolder nacosServerHolder;
private final ClusterAccessService clusterAccessService;
private final SkyWalkerCacheServices skyWalkerCacheServices;
public NacosSyncToNacosServiceImpl(MetricsManager metricsManager, NacosServerHolder nacosServerHolder,
ClusterAccessService clusterAccessService, SkyWalkerCacheServices skyWalkerCacheServices) {
this.metricsManager = metricsManager;
this.nacosServerHolder = nacosServerHolder;
this.clusterAccessService = clusterAccessService;
this.skyWalkerCacheServices = skyWalkerCacheServices;
}
/**
* 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() {
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;
}
Collection<TaskDO> taskCollections = allSyncTaskMap.values();
List<TaskDO> 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.get(taskDO.getSourceClusterId());
int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId());
String taskId = taskDO.getTaskId();
//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<Instance> 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);
}
}
// 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);
return false;
}
return true;
}
@Override
public boolean sync(TaskDO taskDO, Integer index) {
log.info("Thread {} started synchronization at {}", Thread.currentThread().getId(), System.currentTimeMillis());
String taskId = taskDO.getTaskId();
try {
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("Detected changes in service {} information, taskId: {}, number of instances: {}, initiating synchronization",
namingEvent.getServiceName(), taskId,
namingEvent.getInstances() == null ? null : namingEvent.getInstances().size());
try {
doSync(taskId, taskDO, sourceNamingService, destNamingService);
log.info("Detected synchronization end for service {}", namingEvent.getServiceName());
} catch (Exception e) {
log.error("event process fail, taskId:{}", taskId, e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
}
}
});
sourceNamingService.subscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
listenerMap.get(taskId));
} catch (Exception e) {
log.error("sync task from nacos to nacos was failed, taskId:{}", taskId, e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
private void doSync(String taskId, TaskDO taskDO, NamingService sourceNamingService,
NamingService destNamingService) throws NacosException {
if (syncTaskTap.putIfAbsent(taskId, 1) != null) {
log.info("Task ID:{} - the previous synchronization task has not finished yet", taskId);
return;
}
try {
List<Instance> sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(),
getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
int level = clusterAccessService.findClusterLevel(taskDO.getSourceClusterId());
if (CollectionUtils.isNotEmpty(sourceInstances) && sourceInstances.get(0).isEphemeral()) {
// 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)) {
// 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 {
syncTaskTap.remove(taskId);
}
}
private void handlerPersistenceInstance(TaskDO taskDO, NamingService destNamingService,
List<Instance> sourceInstances, int level) throws NacosException {
List<Instance> needRegisterInstance = new ArrayList<>();
for (Instance instance : sourceInstances) {
if (needSync(instance.getMetadata(), level, taskDO.getDestClusterId())) {
needRegisterInstance.add(buildSyncInstance(instance, taskDO));
}
}
List<Instance> destAllInstances = destNamingService.getAllInstances(taskDO.getServiceName(),
getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
// Get the instances that the destination cluster has already synchronized
List<Instance> destHasSyncInstances = destAllInstances.stream()
.filter(instance -> hasSync(instance, taskDO.getSourceClusterId())).collect(Collectors.toList());
// 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<String, Instance> destInstanceMap = destHasSyncInstances.stream()
.collect(Collectors.toMap(NacosSyncToNacosServiceImpl::getInstanceKey, instance -> instance));
// Convert newInstances to a Map with the concatenated string as key
Map<String, Instance> needRegisterMap = needRegisterInstance.stream()
.collect(Collectors.toMap(NacosSyncToNacosServiceImpl::getInstanceKey, instance -> instance));
// Remove instances from newInstanceMap that are present in destInstanceMap
List<Instance> newInstances = removeSyncedInstances(destInstanceMap, needRegisterMap);
// Remove instances from destInstanceMap that are present in newInstanceMap
List<Instance> 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()),
newInstance);
}
if (CollectionUtils.isNotEmpty(invalidInstances)) {
log.info("taskId: {}, service {} deregistered, number of executions: {}", taskDO.getTaskId(), taskDO.getServiceName(),
destHasSyncInstances.size());
}
for (Instance instance : invalidInstances) {
destNamingService.deregisterInstance(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
instance);
}
}
private List<Instance> getInvalidInstances(Map<String, Instance> destInstanceMap, Map<String, Instance> needRegisterMap) {
Map<String, Instance> destClone = new HashMap<>(destInstanceMap);
// Convert newInstances to a Map with the concatenated string as key
Map<String, Instance> needRegisterClone = new HashMap<>(needRegisterMap);
// Remove instances from newInstanceMap that are present in destInstanceMap
destClone.keySet().removeAll(needRegisterClone.keySet());
return new ArrayList<>(destClone.values());
}
public List<Instance> removeSyncedInstances(Map<String, Instance> destInstanceMap, Map<String, Instance> needRegisterMap) {
// Convert destHasSyncInstances to a Map with the concatenated string as key
Map<String, Instance> destClone = new HashMap<>(destInstanceMap);
// Convert newInstances to a Map with the concatenated string as key
Map<String, Instance> 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) {
String sourceClusterKey = instance.getMetadata().get(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY);
return sourceClusterKey != null && sourceClusterKey.equals(sourceClusterId);
}
return false;
}
/**
* 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 destNamingService Destination cluster naming service
* @throws NacosException
*/
private void processDeRegisterInstances(TaskDO taskDO, NamingService destNamingService) throws NacosException {
// If the instances in sourceInstances are empty, it means the instances are offline or do not exist.
List<Instance> 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;
}
destInstances = filterInstancesForRemoval(destInstances, taskDO.getSourceClusterId());
if (CollectionUtils.isNotEmpty(destInstances)) {
// 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);
}
}
}
private List<Instance> filterInstancesForRemoval(List<Instance> destInstances, String sourceClusterId) {
return destInstances.stream().filter(instance -> !instance.getMetadata().isEmpty())
.filter(instance -> needDeregister(instance.getMetadata().get(SOURCE_CLUSTER_ID_KEY), sourceClusterId))
.collect(Collectors.toList());
}
private boolean needDeregister(String destClusterId, String sourceClusterId) {
if (!StringUtils.isEmpty(destClusterId)) {
return destClusterId.equals(sourceClusterId);
}
return false;
}
private boolean needSync(Map<String, String> sourceMetaData, int level, String destClusterId) {
// Regular cluster (default)
if (level == 0) {
return SyncService.super.needSync(sourceMetaData);
}
// 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_CLUSTER_ID_KEY));
}
private Instance buildSyncInstance(Instance instance, TaskDO taskDO) {
Instance temp = getInstance(instance);
temp.addMetadata(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
temp.addMetadata(SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
temp.addMetadata(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
//The flag is a synchronous instance
temp.addMetadata(SkyWalkerConstants.SYNC_INSTANCE_TAG,
taskDO.getSourceClusterId() + "@@" + taskDO.getVersion());
return temp;
}
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());
temp.setServiceName(instance.getServiceName());
temp.setEnabled(instance.isEnabled());
temp.setHealthy(instance.isHealthy());
temp.setWeight(instance.getWeight());
temp.setEphemeral(instance.isEphemeral());
Map<String, String> metaData = new HashMap<>(instance.getMetadata());
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());
}
}

View File

@ -0,0 +1,272 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.SyncService;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.holder.NacosServerHolder;
import com.alibaba.nacossync.extension.holder.ZookeeperServerHolder;
import com.alibaba.nacossync.monitor.MetricsManager;
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 java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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 数据
*
* @author paderlol
* @date 2019年01月06日, 15:08:06
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.NACOS, destinationCluster = ClusterTypeEnum.ZK)
public class NacosSyncToZookeeperServiceImpl implements SyncService {
private final MetricsManager metricsManager;
/**
* @description The Nacos listener map.
*/
private final Map<String, EventListener> nacosListenerMap = new ConcurrentHashMap<>();
/**
* instance backup
*/
private final Map<String, Set<String>> instanceBackupMap = new ConcurrentHashMap<>();
/**
* listener cache of zookeeper format: taskId -> PathChildrenCache instance
*/
private final Map<String, PathChildrenCache> pathChildrenCacheMap = new ConcurrentHashMap<>();
/**
* zookeeper path for dubbo providers
*/
private final Map<String, String> monitorPath = new ConcurrentHashMap<>();
/**
* @description The Sky walker cache services.
*/
private final SkyWalkerCacheServices skyWalkerCacheServices;
/**
* @description The Nacos server holder.
*/
private final NacosServerHolder nacosServerHolder;
private final ZookeeperServerHolder zookeeperServerHolder;
public NacosSyncToZookeeperServiceImpl(SkyWalkerCacheServices skyWalkerCacheServices,
NacosServerHolder nacosServerHolder, ZookeeperServerHolder zookeeperServerHolder, MetricsManager metricsManager) {
this.skyWalkerCacheServices = skyWalkerCacheServices;
this.nacosServerHolder = nacosServerHolder;
this.zookeeperServerHolder = zookeeperServerHolder;
this.metricsManager = metricsManager;
}
@Override
public boolean delete(TaskDO taskDO) {
try {
NamingService sourceNamingService =
nacosServerHolder.get(taskDO.getSourceClusterId());
EventListener eventListener = nacosListenerMap.remove(taskDO.getTaskId());
PathChildrenCache pathChildrenCache = pathChildrenCacheMap.get(taskDO.getTaskId());
sourceNamingService.unsubscribe(taskDO.getServiceName(), getGroupNameOrDefault(taskDO.getGroupName()),
eventListener);
CloseableUtils.closeQuietly(pathChildrenCache);
Set<String> instanceUrlSet = instanceBackupMap.get(taskDO.getTaskId());
CuratorFramework client = zookeeperServerHolder.get(taskDO.getDestClusterId());
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);
metricsManager.recordError(MetricsStatisticsType.DELETE_ERROR);
return false;
}
return true;
}
@Override
public boolean sync(TaskDO taskDO, Integer index) {
try {
NamingService sourceNamingService =
nacosServerHolder.get(taskDO.getSourceClusterId());
CuratorFramework client = zookeeperServerHolder.get(taskDO.getDestClusterId());
nacosListenerMap.putIfAbsent(taskDO.getTaskId(), event -> {
if (event instanceof NamingEvent) {
try {
List<Instance> sourceInstances = sourceNamingService.getAllInstances(taskDO.getServiceName(),
getGroupNameOrDefault(taskDO.getGroupName()), new ArrayList<>(), true);
Set<String> newInstanceUrlSet = getWaitingToAddInstance(taskDO, client, sourceInstances);
// fetch the instance backup
deleteInvalidInstances(taskDO, client, newInstanceUrlSet);
// replace the instance backup
instanceBackupMap.put(taskDO.getTaskId(), newInstanceUrlSet);
// try to compensate for the removed instance
tryToCompensate(taskDO, sourceNamingService, sourceInstances);
} catch (Exception e) {
log.error("event process fail, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
}
}
});
sourceNamingService.subscribe(taskDO.getServiceName(),getGroupNameOrDefault(taskDO.getGroupName()),
nacosListenerMap.get(taskDO.getTaskId()));
} catch (Exception e) {
log.error("sync task from nacos to zk was failed, taskId:{}", taskDO.getTaskId(), e);
metricsManager.recordError(MetricsStatisticsType.SYNC_ERROR);
return false;
}
return true;
}
private void tryToCompensate(TaskDO taskDO, NamingService sourceNamingService, List<Instance> sourceInstances) {
if (!CollectionUtils.isEmpty(sourceInstances)) {
final PathChildrenCache pathCache = getPathCache(taskDO);
// 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<Instance> 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);
}
}
private void deleteInvalidInstances(TaskDO taskDO, CuratorFramework client, Set<String> newInstanceUrlSet)
throws Exception {
Set<String> instanceBackup =
instanceBackupMap.getOrDefault(taskDO.getTaskId(), Sets.newHashSet());
for (String instanceUrl : instanceBackup) {
if (newInstanceUrlSet.contains(instanceUrl)) {
continue;
}
client.delete().quietly().forPath(instanceUrl);
}
}
private HashSet<String> getWaitingToAddInstance(TaskDO taskDO, CuratorFramework client,
List<Instance> sourceInstances) throws Exception {
HashSet<String> waitingToAddInstance = new HashSet<>();
for (Instance instance : sourceInstances) {
if (needSync(instance.getMetadata())) {
String instanceUrl = buildSyncInstance(instance, taskDO);
if (null == client.checkExists().forPath(instanceUrl)) {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
.forPath(instanceUrl);
}
waitingToAddInstance.add(instanceUrl);
}
}
return waitingToAddInstance;
}
protected String buildSyncInstance(Instance instance, TaskDO taskDO) throws UnsupportedEncodingException {
Map<String, String> metaData = new HashMap<>(instance.getMetadata());
metaData.put(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY,
skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
metaData.put(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
String servicePath = monitorPath.computeIfAbsent(taskDO.getTaskId(),
key -> convertDubboProvidersPath(metaData.get(DubboConstants.INTERFACE_KEY)));
return convertDubboFullPathForZk(metaData, servicePath, instance.getIp(), instance.getPort());
}
/**
* fetch zk path cache
*
* @param taskDO task instance
* @return zk path cache
*/
private PathChildrenCache getPathCache(TaskDO taskDO) {
return pathChildrenCacheMap.computeIfAbsent(taskDO.getTaskId(), (key) -> {
try {
PathChildrenCache pathChildrenCache = new PathChildrenCache(
zookeeperServerHolder.get(taskDO.getDestClusterId()), monitorPath.get(key), false);
pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
return pathChildrenCache;
} catch (Exception e) {
log.error("zookeeper path children cache start failed, taskId:{}", taskDO.getTaskId(), e);
return null;
}
});
}
}

View File

@ -0,0 +1,379 @@
/*
* 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.extension.impl;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacossync.cache.SkyWalkerCacheServices;
import com.alibaba.nacossync.constant.ClusterTypeEnum;
import com.alibaba.nacossync.constant.MetricsStatisticsType;
import com.alibaba.nacossync.constant.SkyWalkerConstants;
import com.alibaba.nacossync.extension.SyncService;
import com.alibaba.nacossync.extension.annotation.NacosSyncService;
import com.alibaba.nacossync.extension.holder.NacosServerHolder;
import com.alibaba.nacossync.extension.holder.ZookeeperServerHolder;
import com.alibaba.nacossync.monitor.MetricsManager;
import com.alibaba.nacossync.pojo.model.TaskDO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.utils.CloseableUtils;
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 static com.alibaba.nacossync.util.DubboConstants.ALL_SERVICE_NAME_PATTERN;
import static com.alibaba.nacossync.util.DubboConstants.DUBBO_PATH_FORMAT;
import static com.alibaba.nacossync.util.DubboConstants.DUBBO_ROOT_PATH;
import static com.alibaba.nacossync.util.DubboConstants.GROUP_KEY;
import static com.alibaba.nacossync.util.DubboConstants.INSTANCE_IP_KEY;
import static com.alibaba.nacossync.util.DubboConstants.INSTANCE_PORT_KEY;
import static com.alibaba.nacossync.util.DubboConstants.INTERFACE_KEY;
import static com.alibaba.nacossync.util.DubboConstants.PROTOCOL_KEY;
import static com.alibaba.nacossync.util.DubboConstants.VERSION_KEY;
import static com.alibaba.nacossync.util.DubboConstants.WEIGHT_KEY;
import static com.alibaba.nacossync.util.DubboConstants.ZOOKEEPER_SEPARATOR;
import static com.alibaba.nacossync.util.DubboConstants.createServiceName;
import static com.alibaba.nacossync.util.NacosUtils.getGroupNameOrDefault;
import static com.alibaba.nacossync.util.StringUtils.parseIpAndPortString;
import static com.alibaba.nacossync.util.StringUtils.parseQueryString;
/**
* @author paderlol
* @version 1.0
* @date: 2018-12-24 21:33
*/
@Slf4j
@NacosSyncService(sourceCluster = ClusterTypeEnum.ZK, destinationCluster = ClusterTypeEnum.NACOS)
public class ZookeeperSyncToNacosServiceImpl implements SyncService {
private static final String DEFAULT_WEIGHT = "1.0";
private final MetricsManager metricsManager;
/**
* Listener cache of Zookeeper format taskId -> PathChildrenCache instance
*/
private final Map<String, TreeCache> treeCacheMap = new ConcurrentHashMap<>();
/**
* service name cache
*/
private final Map<String, String> nacosServiceNameMap = new ConcurrentHashMap<>();
private final ZookeeperServerHolder zookeeperServerHolder;
private final NacosServerHolder nacosServerHolder;
private final SkyWalkerCacheServices skyWalkerCacheServices;
public ZookeeperSyncToNacosServiceImpl(ZookeeperServerHolder zookeeperServerHolder,
NacosServerHolder nacosServerHolder, SkyWalkerCacheServices skyWalkerCacheServices,
MetricsManager metricsManager) {
this.zookeeperServerHolder = zookeeperServerHolder;
this.nacosServerHolder = nacosServerHolder;
this.skyWalkerCacheServices = skyWalkerCacheServices;
this.metricsManager = metricsManager;
}
@Override
public boolean sync(TaskDO taskDO, Integer index) {
try {
if (treeCacheMap.containsKey(taskDO.getTaskId())) {
return true;
}
if (!initializeTreeCache(taskDO)) {
return false;
}
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);
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);
return false;
}
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<String, String> 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<String> 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<String> 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<String> serviceNames, NamingService destNamingService, TaskDO taskDO) {
for (String serviceName : serviceNames) {
try {
List<Instance> 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<String, String> queryParam) {
return isVersionMatch(taskDO, queryParam) &&
isGroupMatch(taskDO, queryParam) &&
isServiceMatch(taskDO, queryParam) ||
isMatchAllServices(taskDO);
}
private boolean isVersionMatch(TaskDO task, Map<String, String> queryParam) {
return StringUtils.isBlank(task.getVersion()) || StringUtils.equals(task.getVersion(), queryParam.get(VERSION_KEY));
}
private boolean isGroupMatch(TaskDO task, Map<String, String> queryParam) {
return StringUtils.isBlank(task.getGroupName()) || StringUtils.equals(task.getGroupName(), queryParam.get(GROUP_KEY));
}
private boolean isServiceMatch(TaskDO task, Map<String, String> 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<String, String> queryParam, Map<String, String> 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<String, String> 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<String, String> buildMetadata(Map<String, String> queryParam, Map<String, String> ipAndPortMap, TaskDO taskDO) {
Map<String, String> metaData = new HashMap<>(queryParam);
metaData.put(PROTOCOL_KEY, ipAndPortMap.get(PROTOCOL_KEY));
metaData.put(SkyWalkerConstants.DEST_CLUSTER_ID_KEY, taskDO.getDestClusterId());
metaData.put(SkyWalkerConstants.SYNC_SOURCE_KEY, skyWalkerCacheServices.getClusterType(taskDO.getSourceClusterId()).getCode());
metaData.put(SkyWalkerConstants.SOURCE_CLUSTER_ID_KEY, taskDO.getSourceClusterId());
return metaData;
}
private void processEvent(TaskDO taskDO, NamingService destNamingService, TreeCacheEvent event, String path,
Map<String, String> queryParam) throws NacosException {
if (!com.alibaba.nacossync.util.StringUtils.isDubboProviderPath(path)) {
return;
}
Map<String, String> 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:
destNamingService.registerInstance(getServiceNameFromCache(serviceName, queryParam),
getGroupNameOrDefault(taskDO.getGroupName()), instance);
break;
case NODE_REMOVED:
destNamingService.deregisterInstance(getServiceNameFromCache(serviceName, queryParam),
getGroupNameOrDefault(taskDO.getGroupName()), ipAndPortParam.get(INSTANCE_IP_KEY),
Integer.parseInt(ipAndPortParam.get(INSTANCE_PORT_KEY)));
nacosServiceNameMap.remove(serviceName);
break;
default:
break;
}
}
private void registerAllInstances(TaskDO taskDO, NamingService destNamingService) throws Exception {
CuratorFramework zk = zookeeperServerHolder.get(taskDO.getSourceClusterId());
if (!ALL_SERVICE_NAME_PATTERN.equals(taskDO.getServiceName())) {
registerALLInstances0(taskDO, destNamingService, zk, taskDO.getServiceName());
} else {
// 同步全部
List<String> serviceList = zk.getChildren().forPath(DUBBO_ROOT_PATH);
for (String serviceName : serviceList) {
registerALLInstances0(taskDO, destNamingService, zk, serviceName);
}
}
}
private void registerALLInstances0(TaskDO taskDO, NamingService destNamingService, CuratorFramework zk,
String serviceName) throws Exception {
String path = String.format(DUBBO_PATH_FORMAT, serviceName);
if (zk.getChildren() == null) {
return;
}
List<String> providers = zk.getChildren().forPath(path);
for (String provider : providers) {
Map<String, String> queryParam = parseQueryString(provider);
if (isMatch(taskDO, queryParam) && needSync(queryParam)) {
Map<String, String> ipAndPortParam = parseIpAndPortString(path + ZOOKEEPER_SEPARATOR + provider);
Instance instance = buildSyncInstance(queryParam, ipAndPortParam, taskDO);
destNamingService.registerInstance(getServiceNameFromCache(serviceName, queryParam),
getGroupNameOrDefault(taskDO.getGroupName()), instance);
}
}
}
/**
* cteate Dubbo service name
*
* @param serviceName dubbo service name
* @param queryParam dubbo metadata
*/
protected String getServiceNameFromCache(String serviceName, Map<String, String> queryParam) {
return nacosServiceNameMap.computeIfAbsent(serviceName, (key) -> createServiceName(queryParam));
}
}

Some files were not shown because too many files have changed in this diff Show More