这里写目录标题
一级目录
二级目录
三级目录
一. 概述
最近公司在做组件的技术升级,需要将原有注册中心 Eureka,配置中心 Spring Cloud Config 替换为 Nacos,主要原因如下:
1. Spring Cloud Netflix Eureka项目宣布闭源
Spring Cloud的这一重要宣布,意味着,随着技术的发展,最终使用Eureka的用户都会面临注册中心迁移的问题。
2. 原生的SpringCloud-config以为以Git作为存储介质的硬伤
①Git的权限控制是个坑
Git的权限管理是说控制用户能不能Push或者Delete分支,或者能不能Push代码,而不是能不能访问某个目录的文件。
对目录和文件的可读是Git的最基本要求,不可能做到针对目录级别的不可读。
因此如果直接使用,会出现这样一种情形
②不同团队之间可以互看对方配置!
于是,可能会有如下情形发生
A团队同事A:“小B,这个地方不懂怎么配?”
A团队同事B:“去看看B团队的配置,直接贴过来。”
然后B团队就会发现自己的中间件里总是会多出一些莫名奇妙的数据!
当然,你可以禁止研发直接登陆Git改配置。然后呢,基于Git研发一套配置管理系统,在上面做权限控制,但是又有几个公司这么做呢?因为,这可能带来第二个问题。
③粒度问题
将配置信息放在Git中,有一个致命问题:粒度太粗了!
你每次对一条配置发生crud的操作,其带来的影响是整个文件发生变动。如果将来我们需要对某条配置做灰度发布,基于Git来做是比较麻烦的,注意了,我没说不能做,只是比较麻烦。
那么,当时我们最理想的存储介质就是数据库,将配置信息放在数据库里有两个好处
基于数据库开发一套配置管理系统,显然比基于git来开发容易的多!
将配置放在数据库里,每条配置对应数据库的表中的一条记录。这么做粒度够细,针对某些重要的配置,做灰度发布,实现起来就容易很多。
因此,我们采用数据库作为存储介质。庆幸的是,这一点在Spring-Cloud-Config中是支持的。在该组件下,只需要设置
spring.profiles.active=jdbc
就能够激活jdbc模式。
但是我们很快发现了一个更大的问题,也正是因为这个问题,我们不得以需要进行改写Spring-Cloud-Config。
④Spring-Cloud-Config的刷新机制是个坑!
因为一个配置中心应该要能够做到,配置发生改动的时候,项目能够自动感知,自动更新配置才对。在Spring-Cloud-Config中,这套机制是借助一些代码仓库(SVN、Github等)提供的Webhook机制加上Spring-Cloud-Bus来实现的。
在Webhook中配置一个回调地址,刷新流程如下图所示:
OK,那么问题又来了!
(1)配置数据放在数据库中,数据库里没有Webhook这种东西啊,怎么做到实时刷新?
(2)Spring-Cloud-Config的这套刷新机制依赖于消息总线,依赖于消息队列,存在延迟的情况!且依赖于消息队列的可用性,系统的复杂度大大增加。如果生产环境上消息队列出问题了,我们的刷新功能就会受到影响!
3. Nacos更强
<1> 在服务注册上,Nacos 几乎实现了 Eureka 所支持的所有特性;
<2> 在配置管理上,Nacos 具备 Config 所支持的所有功能;
<3> Nacos 提供了控制台UI,便于更方便地进行服务管理,配置管理;
可以理解为,Nacos = Spring Cloud Eureka + Spring Cloud Config,但是其功能更强大。
因此,本文将探讨:如何以 “零代码” 的方式,将使用 Eureka,Config 的应用迁移到 Nacos 上。
二Nacos
Nacos=Netflix-Eureka+SpringCloud-Config
nacos地址
https://github.com/alibaba/nacos/releases/tag/1.3.2
三服务注册中心无痛迁移过程
1关于 eureka和nacos的服务注册中心
eureka实例规模在很大的时候才会出现性能瓶颈,所以服务在规模合适的情况下其性能不会存在问题,
一般情况下可以使用eureka1.0作为注册中心使用,满足业务使用即可。
nacos在一般项目性能上与EURKA相当,在伸缩性和扩展性来看Nacos支持跨注册中心同步及大数量级的
集群;nacos中包含了配置中心,在源码中可以看到在客户端维护了一个长轮询的任务,会通过安全监听
通知变更的内容定拉取发生变更的配置信息。
eureka的数据同步采取的是去除中心化,每个节点都持有全量数据,新增的数据可以往任
意一个节点写入,然后由这个节点向其他节点传播达到一致性。
nacos使用一个数据同步中间件,Nacos-Sync用来做数据同步中心服务。
2组件版本关系前期准备
3安装 NacosServer高可用 集群代替EurekaServer高可用集群
nacosserver不像eureka一样,可以通过依赖的方式引入到项目中,并且可以整合在项目中配置运行,因为nacos(eureka+springcloudConfig)整体的体积较大,所以需要单独下载部署,微服务模块使用开发过程中需要删除eurekaserver的集群模块。
①解压 nacos-server-1.3.0.tar.gz。
②修改application.properties,文件位于nacos/conf目录下,修改文件中的数据库配置。
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://10.0.17.21:3306/registry?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
③修改cluster.conf,文件位于nacos/conf目录下,配置集群节点的信息(IP端口形式)。
#it is ip
#example
10.0.112.64:8848
10.0.112.65:8848
10.0.112.66:8848
④启动
进入nacos/bin目录下,通过 sh startup.sh 启动 nacos 即可。
4修改服务提供者模块
①依赖
eureka client依赖
<!--springcloud的Finchley.SR2版本新的eureka客户端依赖-->
<<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
nacos client依赖
<!--nacos客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
②配置
eureka
#注意不要忘了/eureka/后缀,设置与eureka-server的交互地址
eureka.client.service-url.defaultZone= http://eurekaserver1:9001/eureka/,http://eurekaserver2:9002/eureka/,http://eurekaserver3:9003/eureka/
#eureka的健康检查机制开关
eureka.client.healthcheck.enabled=true
#自我保护机制
#eureka.server.enable-self-preservation=false
#自我保护机制关闭之后的配置,告诉服务端,如果你N秒之后还没有接受到我发送的心跳,就代表我"死"了,将我的服务踢出掉。
#eureka.instance.lease-expiration-duration-in-seconds=36
#设置每隔多少秒给服务中心发送一次心跳,证明自己还活着
#eureka.instance.lease-renewal-interval-in-seconds=6
#springcloudconfig的配置中心开关,SpringCloud默认启动的时候是会先加载bootstrap.properties或bootstrap.yml配置文件,如果没有的话,则会远程从http://localhost:8888获取配置,然后才会加载到application.yml,properties文件。
spring.cloud.config.enabled=false
nacos
#springcloudconfig的配置中心开关,SpringCloud默认启动的时候是会先加载bootstrap.properties或bootstrap.yml配置文件,如果没有的话,则会远程从http://localhost:8888获取配置,然后才会加载到application.yml,properties文件。
spring.cloud.config.enabled=false
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.config.server-addr=localhost:8848
③启动类,更换注解即可
@EnableEurekaClient》》》》》》@EnableDiscoveryClient
eureka
package fengbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Created by @author LiuChunhang on 2020/7/14.
*/
@MapperScan(basePackages = "fengbo.dao")
@EnableTransactionManagement
@SpringBootApplication
@EnableEurekaClient
public class AccountClientApp {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(AccountClientApp.class);
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.run(args);
}
}
nacos
package fengbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Created by @author LiuChunhang on 2020/7/14.
*/
@MapperScan(basePackages = "fengbo.dao")
@EnableTransactionManagement
@SpringBootApplication
/*@EnableEurekaClient*/
@EnableDiscoveryClient
public class AccountClientApp {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(AccountClientApp.class);
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.run(args);
}
}
5修改服务消费者模块
①依赖
eureka client依赖
<!--springcloud的Finchley.SR2版本新的eureka客户端依赖-->
<<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
nacos client依赖
<!--nacos客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
②配置
eureka
#禁止自己当做服务注册,微服务消费者只消费服务,不像注册中心注册,如果加网关组件的话,必须开启自注册,消费者供网关消费
#eureka.client.register-with-eureka=false
#指定向哪些eurekaserver注册
eureka.client.service-url.defaultZone=http://eurekaserver3:9003/eureka/,http://eurekaserver2:9002/eureka/,http://eurekaserver1:9001/eureka/
nacos
#springcloudconfig的配置中心开关,SpringCloud默认启动的时候是会先加载bootstrap.properties或bootstrap.yml配置文件,如果没有的话,则会远程从http://localhost:8888获取配置,然后才会加载到application.yml,properties文件。
spring.cloud.config.enabled=false
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.config.server-addr=localhost:8848
③ 启动类,更换注解即可
eureka
package fengbo;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import ribbonbalance.IndividualBalance;
/**
* Created by @author LiuChunhang on 2020/7/21.
*/
@SpringBootApplication
@EnableEurekaClient
/**此注解的属性用于服务定制化负载均衡,且负载均衡配置类不能在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制的目的了。*/
@RibbonClient(name = "accountprovider",configuration = IndividualBalance.class)
@EnableFeignClients(basePackages = "fengbo.service")
public class AccountSystem {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AccountSystem.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
nacos
package fengbo;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import ribbonbalance.IndividualBalance;
/**
* Created by @author LiuChunhang on 2020/7/21.
*/
@SpringBootApplication
@EnableDiscoveryClient
/**此注解的属性用于服务定制化负载均衡,且负载均衡配置类不能在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制的目的了。*/
@RibbonClient(name = "accountprovider",configuration = IndividualBalance.class)
@EnableFeignClients(basePackages = "fengbo.service")
public class AccountSystem {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AccountSystem.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
6服务中心测试
①下载,解压缩,执行命令部署Nacos
https://github.com/alibaba/nacos/releases
,--.
,--.'|
,--,: : | Nacos
,`--.'`| ' : ,---. Running in stand alone mode, All function modules
| : : | | ' ,'\ .--.--. Port: 8848
: | \ | : ,--.--. ,---. / / | / / ' Pid: 11836
| : ' '; | / \ / \. ; ,. :| : /`./ Console: http://192.168.1.34:8848/nacos/index.html
' ' ;. ;.--. .-. | / / '' | |: :| : ;_
| | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io
' : | ; .' ," .--.; |' ; :__| : | `----. \
| | '`--' / / ,. |' | '.'|\ \ / / /`--' /
' : | ; : .' \ : : `----' '--'. /
; |.' | , .-./\ \ / `--'---'
'---' `--`---' `----'
2020-08-13 16:23:07,306 INFO Bean 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration$$EnhancerBySpringCGLIB$$d877e6a8] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-08-13 16:23:07,646 INFO Bean 'objectPostProcessor' of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-08-13 16:23:07,656 INFO Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@73a8da0f' of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-08-13 16:23:07,657 INFO Bean 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$fd4c895a] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-08-13 16:23:07,679 INFO Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-08-13 16:23:08,951 INFO Tomcat initialized with port(s): 8848 (http)
2020-08-13 16:23:09,510 INFO Root WebApplicationContext: initialization completed in 8928 ms
2020-08-13 16:23:17,205 INFO Initializing ExecutorService 'applicationTaskExecutor'
2020-08-13 16:23:17,423 INFO Adding welcome page: class path resource [static/index.html]
2020-08-13 16:23:18,434 INFO Creating filter chain: Ant [pattern='/**'], []
2020-08-13 16:23:18,640 INFO Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@ecf9fb3, org.springframework.security.web.context.SecurityContextPersistenceFilter@60d84f61, org.springframework.security.web.header.HeaderWriterFilter@41c07648, org.springframework.security.web.csrf.CsrfFilter@52c8295b, org.springframework.security.web.authentication.logout.LogoutFilter@2575f671, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@324dcd31, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@433ffad1, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2d35442b, org.springframework.security.web.session.SessionManagementFilter@781e7326, org.springframework.security.web.access.ExceptionTranslationFilter@77b21474]
2020-08-13 16:23:19,012 INFO Exposing 2 endpoint(s) beneath base path '/actuator'
2020-08-13 16:23:19,062 INFO Initializing ExecutorService 'taskScheduler'
2020-08-13 16:23:19,297 INFO Tomcat started on port(s): 8848 (http) with context path '/nacos'
2020-08-13 16:23:19,307 INFO Nacos Log files: D:\AlibabaNacos\logs
2020-08-13 16:23:19,309 INFO Nacos Log files: D:\AlibabaNacos\conf
2020-08-13 16:23:19,310 INFO Nacos Log files: D:\AlibabaNacos\data
2020-08-13 16:23:19,310 INFO Nacos started successfully in stand alone mode. use embedded storage
②出现如下界面,证明部署成功。
账号,密码都是 nacos
7服务发现调用测试
启动已经配置好的服务提供者,然后登录到nacosserver,见如下界面,则证明服务提供者配置与nacos的注册中心成功!
负载均衡调用微服务测试
这样子,就完成了服务注册与发现调用的改造。
四服务配置中心无痛人流迁移过程
1服务提供者和消费者更换依赖
在前面的基础上,直接引入下面依赖即可:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
同时去掉 SpringCloudConfig 的客户端依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2服务提供者和消费者更换配置
#Nacos配置中心URL
spring.cloud.nacos.config.server-addr=localhost:8848
同时去掉 SpringCloudConfig 的配置
spring.cloud.config.enabled=false
spring.cloud.config.config.server.git=XXXX
这样子,服务就加入了Nacos配置中心。
五. 改造带来的收益
改造完成后,经过多轮测试,最终成功投产到生产环境。经过一段时间的验证,我们发现Nacos还是很香的。
1. 经济价值
我们替换了旧的配置和注册中心,目前开发测试共有5套环境,每套环境的配置和注册中心都部署集群的话,需要部署522=20个节点,现在只需要部署3个节点,即可实现高可用。这为我们省下了一笔机器的花销。
2. 实用价值
<1> 服务管理: 随时随地上下线应用,随时随地修改权重。
<2> 配置管理: 高效管理配置,支持修改对比,回滚配置,并且记录修改记录。
要知道,这两个功能,在Eureka均需要二次开发,这对中小企业来说,不够友好。而Nacos天然地支持服务,配置管理。
3. 其他价值
使用Nacos后,我们的应用完美融入了微服务生态。我们知道,Eureka和Config在融入云原生方面存在很大欠缺(虽然有解决方案,但较为繁琐)。而Nacos则不同,它天然地融入云原生中,据官方消息,Nacos将为融入Service Mesh做出更大改进。
六. 总结
经过上述改造,服务得以顺利地从 Eureka,Config 迁移到 Nacos 上,后续在微服务生态上能发挥更好的价值。
下面做个简单的总结:
-
Spring Cloud Alibaba 实现了Spring Cloud服务注册规范,这使得开发者能够无痛地迁移 Eureka 到 Nacos;
-
在功能上,Nacos = Eureka + Nacos;
-
在生态上,Nacos亲近开发者,无论是对dubbo,SpringCloud还是k8s的兼容,深得人心;
因此,可以相信,随着技术的演进, Nacos 一定会在微服务体系上走得更远,走得更好。