Spring Cloud 项目平滑将注册中心迁移到Nacos上
1.问题概述
由于项目升级,需要将Eureka注册中心改为阿里的Nacos作为注册中心
项目环境
1、Java 使用的 JDK-1.8
2、Spring Boot 使用的 Spring Boot-2.1.4.RELEASE
3、Spring Cloud 使用的是 Greenwich.RELEASE
2.方案设计
2.1 业务应用多注册到Nacos和Eureka
Spring Cloud应用默认不支持启动时双向注册,但是阿里商业版上云edas-sc-migration-starter组件支持。可以采用如下的方式引入依赖实现多注册和订阅。
<!-- https://mvnrepository.com/artifact/com.alibaba.edas/edas-sc-migration-starter -->
<dependency>
<groupId>com.alibaba.edas</groupId>
<artifactId>edas-sc-migration-starter</artifactId>
<version>1.0.2</version>
</dependency>
也就是引入对应的组件,当应用启动的时候同时向Eureka和Nacos实现双向注册。
如上图所示,如果只把旧应用只改一部分,会出现只有改造的应用能调到新应用。未改造的应用会出现调不到新应用的情况。即,需要如下图所示的方式,旧应用全部升级改造为双注册到注册中心,才可以支持。
但是旧应用无法保证同一时刻全部升级改造为同时注册,因此该方案论证失败。
2.2 Nacos Sync方案
手册目标
- 启动 NacosSync 服务
- 通过一个简单的例子,演示如何将注册到 Zookeeper 的 Dubbo 客户端迁移到 Nacos。
准备工作
启动服务之前,你需要安装下面的服务:
- 64bit OS: Linux/Unix/Mac/Windows supported, Linux/Unix/Mac recommended.
- 64bit JDK 1.8+: downloads, JAVA_HOME settings.
- Maven 3.2.x+: downloads, settings.
- MySql 5.6.+
获取安装包
有两种方式可以获得 NacosSync 的安装包:
- 直接下载 NacosSync 的二进制安装包:nacosSync.${version}.zip
- 从 GitHub 上下载 NacosSync 的源码进行构建
Package:
cd nacosSync/
mvn clean package -U
目标文件的路径:
nacos-sync/nacossync-distribution/target/nacosSync.${version}.zip
解压安装包之后,工程的文件目录结构:
nacosSync
├── LICENSE
├── NOTICE
├── bin
│ ├── nacosSync.sql
│ ├── shutdown.sh
│ └── startup.sh
├── conf
│ ├── application.properties
│ └── logback-spring.xml
├── logs
└── nacosSync-server.${version}.jar
初始化数据库
系统默认配置的数据库是Mysql,也能支持其他的关系型数据库。 1.建库,缺省的数据库名字为“nacos_Sync”。 2.数据库表不需要单独创建,默认使用了hibernate的自动建表功能。 3.如果你的环境不支持自动建表,可以使用系统自带的sql脚本建表,脚本放在bin目录下。
数据库配置
数据库的配置文件放在conf/application.properties
中:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/nacos_sync?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
启动服务器
$ nacosSync/bin:
sh startup.sh restart
检查系统状态
1.检查系统日志
日志的路径在nacosSync/logs/nacosSync.log
,检查是否有异常信息。
2.检查系统端口
缺省的系统端口是8081
,你可以自己定义在application.properties
中。
$netstat -ano|grep 8081
tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN off (0.00/0/0)
控制台
访问路径:
http://127.0.0.1:8081/#/serviceSync
如果检查没有问题,NacosSync 已经正常启动了,NacosSync 的部署结构:
开始迁移
迁移信息
Dubbo服务的部署信息:
迁移的服务:
Service Name | Version | Group Name |
com.alibaba.nacos.api.DemoService | 1.0.0 | zk |
添加注册中心集群信息
1.点击左侧导航栏中的“集群配置”按钮,新增加一个集群,先增加一个Zookeeper集群,选择集群类型为ZK。
注意:集群名字可以自定义,但是一旦确认,不能被修改,否则基于此集群增加的任务,在 NacosSync 重启后,将不会恢复成功。
2.同样的步骤,增加NacosSync集群。
3.添加完成后,可以在列表中查询到:
添加同步任务
1.增加一个同步任务,从Zookeeper集群同步到Nacos集群,同步的粒度是服务,Zookeeper集群则称为源集群,Nacos集群称为目标集群。
添加完成之后,可以在服务同步列表中,查看已添加的同步任务:
2.同步完成之后,检查下数据是否同步成功到Nacos集群,可以通过Nacos的控制台进行查询。
3.此刻,数据已经成功从Zookeeper集群同步到了Nacos集群,部署结构如下:
2.3 注册中心服务端双向同步
2.3.1 方案设计
设计思路是注册中心服务端进行双向同步,做到微服务端完全无侵入,可以随业务迭代逐步完成升级和迁移。改造Eureka Server,Eureka Server引入同步组件实现Nacos和Eureka之间实现双向同步,如下图所示:
2.3.2 迁移步骤
迁移步骤如下:
- 1.部署Nacos Server集群用于服务注册与发现
- 2.在线动态扩容Eureka Server,替换其中的1-2两台Eureka Server。
- 3.逐渐改造旧应用,只需将新旧应用注册到Nacos上
- 4.等旧应用全部改造完毕,下线Eureka Server即可。
这样方案的优点,如下:
- 1.新应用直接注册到Nacos上,不需要同时注册到Eureka和Nacos上
- 2.旧应用直接改造(引入相关starter即可)注册到Nacos上即可,不需要同时注册到Nacos和Eureka上
- 3.迁移成本很低,旧应用只需改造一次(所谓的改造即引入新的Starter,修改配置),等全部旧应用迁移完毕,直接下线Eureka Server。
3.迁移落地
3.1 组件开发
开发一个Spring Boot Starter,加入到Eureka Server或Nacos Server中可以实现双向注册。
3.2 spring-cloud-nacos组件介绍
- spring-cloud-nacos主要由
何鹰和瞿礼
贡献, - Nacos Plus在Nacos上做加法,Nacos client默认是支持安全控制的可以参考阿里云商业版配置中心ACM集成方式 https://github.com/alibaba/spring-cloud-alibaba/wiki/ACM 以及 spring cloud alibaba 配置项 https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md 但是开源的Nacos并不含有安全控制模块,在Nacos plus里我们新增了兼容默认Nacos client的安全控制功能。
可以查看:https://github.com/inacos/nacos
核心代码主要是NacosSynchronizer.java和EurekaSynchronizer.java,请自行阅读。 github地址:https://github.com/inacos/spring-cloud-nacos
4.实现迁移落地
按如下步骤
1.部署Nacos Server集群用于服务注册与发现
2.在线动态扩容一台Eureka Server
3.逐渐改造旧应用,只需将新旧应用注册到Nacos上
4.等旧应用全部改造完毕,下线Eureka Server即可。
下面将在本地进行测试说明,用到的演示说明列表如下所示
服务 | 说明 |
Eureka注册中心(http://localhost:10001) | 模拟生产上的Eureka注册中心 |
本地基于源码方式启动一个Nacos实例 | 模拟生产上新建的Nacos |
同步服务(本地基于Eureka Server扩展启动的同步服务) | 模拟生产上启动了一个Nacos与Eureka之间双向同步的服务 |
本地启动一个服务注册到(http://localhost:10001) | 验证从eureka同步服务到nacos |
本地启动一个服务注册到Nacos | 验证从nacos同步服务到Eureka上 |
4.1 模拟生产上的注册中心
4.2 基于源代码方式启动Nacos
你可以通过源码和发行包两种方式来获取 Nacos。
从Github上面下载源码
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
下载编译后压缩包方式
您可以从 最新稳定版本 下载 nacos-server-$version.zip
包。
unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
cd nacos/bin
基于Nacos的最新Master代码,本地IDEA设置环境变量-Dnacos.standalone=true,启动Nacos,如下所示:
2.打开浏览器访问 http://localhost:8848/nacos/index.html,输入用户名和密码(均为nacos)登录。
4.3 启动同步服务测试
4.3.1 搭建同步服务
将github上面的源码下载到本地,地址:https://github.com/inacos/spring-cloud-nacos
- 如GitHub示例工程spring-cloud-nacos-eureka-proxy-example(相当于我们的Eureka Server端)所示,在一个Eureka Server中引入如下的pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入Nacos与Eureka之间同步的Starter-->
<dependency>
<groupId>net.nacos</groupId>
<artifactId>spring-cloud-nacos-eureka-proxy</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
- 在application.properties配置如下信息
server.port=10001
spring.application.name=eureka-proxy
#配置Nacos注册中心的地址
spring.cloud.nacos.discovery.serverAddr=localhost:8848
eureka.client.serviceUrl.defaultZone=http://localhost:10001/eureka/
#eureka.client.fetchRegistry=false
#eureka.client.registerWithEureka=false
eureka.instance.preferIpAddress=true
eureka.server.enable-self-preservation=false
4.3.2 启动服务
启动同步服务,访问Nacos页面发现已经把Eureka上面的服务同步到Nacos,如下所示:
4.3.3 验证从Eureka同步到Nacos
启动一个xxxx-gateway的服务只注册到 http://localhost:10001/,立即同步到Nacos上.
4.3.4 验证从Nacos同步到Eureka
启动一个服务一个服务只注册到Nacos上,发现立即同步到 [http://eureka.springcloud.cn/上],自行验证.
5. 旧服务改造,注册到Nacos上
5.1. 注释掉工程和服务模块里的eureka相关依赖
<!--注册中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
5.2. 添加nacos依赖
说明:由于springcloud和springboot对应nacos有对应的版本
版本 2.1.x.RELEASE 对应的是 Spring Boot 2.1.x 版本。
版本 2.0.x.RELEASE 对应的是 Spring Boot 2.0.x 版本,
版本 1.5.x.RELEASE 对应的是 Spring Boot 1.5.x 版本。
更多版本对应关系参考:版本说明 Wiki
<properties>
<nacos.version>2.1.0.RELEASE</nacos.version>
</properties>
</dependencyManagement>
</dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
</dependency>
5.3. springboot启动类注释掉eureka的注解,添加nacos服务发现注解
@EnableDiscoveryClient
5.4. 添加nacos相关配置
1. 新建bootstrap.properties配置文件(如果想用yml/yaml,改成对应的后缀即可;注意:如果此处用的yaml/yml,nacos里的配置文件也得用yaml/yml),配置信息如下:
spring.application.name=xxx-gateway
#spring.profiles.include=swagger,mysql
server.port=8089
# nacos配置
spring.cloud.nacos.config.group=IDS_GATEWAY_GROUP
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.discovery.server-addr=localhost:8848
#配置nacos动态刷新
spring.cloud.nacos.config.refresh-enabled=true
#配置nacos环境
spring.cloud.nacos.config.namespace=dev
spring.cloud.nacos.discovery.namespace=dev
spring.main.allow-bean-definition-overriding=true
2.进入nacos配置管理台,配置nacos的dataId,Group等信息,编写应用程序的配置文件,支持yaml,yml,properties
3.如果需要配置文件自动刷新,则在启动类上添加@RefreshScope注解即可
localhost:8848
spring.cloud.nacos.discovery.server-addr=localhost:8848
#配置nacos动态刷新
spring.cloud.nacos.config.refresh-enabled=true
#配置nacos环境
spring.cloud.nacos.config.namespace=dev
spring.cloud.nacos.discovery.namespace=dev
spring.main.allow-bean-definition-overriding=true