深圳幻海软件技术有限公司 欢迎您!

迁移 Eureka 到 Nacos 之双注册双订阅模式

2023-02-28

这里面涉及到这个双注册双订阅模式,下面让我们一起看看吧!内容概览首先,为啥要迁移呢?主要是它对比其他注册中心,已经落后太多了。就拿Nacos来说吧,不仅有配置中心,管理界面,还能手动上下线,而且支持服务列表变更的消息推送模式(实时性高)。Eureka1.x的架构有些地方可以改进,比如在客户端的pul

这里面涉及到这个 双注册双订阅模式 ,下面让我们一起看看吧!

内容概览

首先,为啥要迁移呢?

主要是它对比其他注册中心,已经落后太多了。

  • 就拿 Nacos 来说吧,不仅有 配置中心,管理界面,还能手动上下线,而且支持服务列表变更的消息推送模式(实时性高)。
  • Eureka 1.x 的架构有些地方可以改进,比如 在客户端的 pull 模式下,增加这个消息推送模式,增加实时性;还有 集群,Eureka 只支持 AP ,各个客户端都能进行写请求 , 没有主从节点之分,各个节点之间通过相互复制来同步数据,无法保证一致性。Nacos 则有 AP 和 CP 两种选择,更灵活。
  • 2019年的某个会上,Spring 团队提出如何解决 Netflix 进入维护模式后的 SpringCLoud 组件选择问题。
  • 就是 Eureka 早已进入维护模式啦!而且 long long ago ,官方就放弃了这个 Eureka2.X 版本的开发(看了下分支,6,7年前的代码了),而且官方还说了不能用在生产上,后果自负(咱也不知道它有啥新特点,反正 SpringCloud 一直用的 1.x 版本。现在更新到1.10 了)。

官方Wiki

Eureka 最新版本

简单了解了这个背景后,咱们再来看看 4ye 搭建的这个 demo 。

Eureka 注册中心

比如 老项目中,使用的注册中心是 Eureka 。架构如下:

代码也很简单,有三个模块。分别是

  • 注册中心 :搭建时选择 Eureka Server 即可。
  • 服务提供者(provider):搭建时选择 Eureka Discovery Client 和 Spring Web 即可。
  • 服务消费者(consumer):搭建时选择 Eureka Discovery Client 和 Spring Web 即可。

然后依次启动注册中心,provider,consumer 即可。

访问 http://localhost:8772/hello/Java4ye 可以看到下面的内容。

双注册双订阅模式

接着,我们克隆上面的 provider 和 consumer 模块。

在 pom 文件中直接加入 Nacos 和 Eureka 。

启动时会抛出下面的异常信息 。(引入 Actuator 时会出现另一个,同样排除掉即可)

Description:

Field autoServiceRegistration in org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration required a single bean, but 2 were found:
  - nacosAutoServiceRegistration: defined by method 'nacosAutoServiceRegistration' in class path resource [com/alibaba/cloud/nacos/registry/NacosServiceRegistryAutoConfiguration.class]
  - eurekaAutoServiceRegistration: defined by method 'eurekaAutoServiceRegistration' in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.class]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

可以看到是自动装配时,不知道用哪个导致的异常。但是我们两个都要。

这里只要在 application.properties 中把这个自动装配移除掉即可。

# 双注册模式下关闭
spring.autoconfigure.exclude=org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration,org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration
  • 1.
  • 2.

当然,秉着严谨的态度,我们在这两个类中打入相应的断点,可以看到他们都被创建了。

同时,也成功注册到这两个注册中心去了。

Eureka

Nacos

这个时候,再次访问旧的客户端 8772 端口的,可以发现如下效果。

但是要注意,此时项目的架构变成这样,consumer 中只有 Eureka 的客户端,所以调用到的都是 Eureka 中心中的服务。此时流量不会走到 Nacos 这边

接着便是看客户端 consumer 正不正常,比如跑个一天看看。

稳定后,下一步就是 下线这个 provider ,然后看看正不正常了。同样稳定后,便是准备启动这个 双订阅的客户端了。

小实验

但是我这里做了一个小实验 哈哈 想看看不下线的情况,我这个 新客户端 上线后是使用哪个注册中心的服务多点。

所以,接着,我们就启动这个新的 consumer,一个双订阅的客户端。

同样修改下配置文件即可。

spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=192.168.175.128:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public

# 双注册模式下关闭
spring.autoconfigure.exclude=org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration,org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

这个时候,我们访问新的客户端。8872 端口的:http://localhost:8872/hello/Java4ye

发现无论怎么刷新,接口的返回值都是下面这个,无法达到负载均衡的效果 。(⊙o⊙)?

简单翻看了下源码,可以发现系统创建了三个 discoveryClient ,最后一个是兜底用的。

而且 nacos 排在第一个,这意味着从 nacos 的注册中心中找到服务的话,就不会调用到 Eureka 中的了。

了解了这个原理后,将 nacos 中的服务进行下线。

然后去刷新新的客户端,8872 端口的,可以发现,又出现了负载均衡的效果了。

而且得益于 Nacos 的服务列表变更推送机制,我们客户端可以实时感知到 服务列表的 变化,这个时候直接去刷新新客户端的接口,可以发现它已经切换到 Eureka 中了,没有延迟感!

所以当我们在迁移的过程中,如果发现 Nacso 上新的 provider 有什么异常时,可以将其下线先🐷 轻轻一点真的太方便了。

优雅下线

结束上面的小实验,回到正常流程中,我们要来下线这个 provider 了。

阶段目标

这里就得考虑这个 优雅下线 的问题了。

网上的方案很多,这里用 Springboot 的 actuator 来实现。

这个 graceful 配置是 Springboot2.3 之后才有的,会让内嵌服务器拒绝外部请求,然后处理完已经在内部的请求后,进入关闭状态。

通过这个暴露的 api,去修改 eureka 中 service 的状态。

# 优雅下线
server.shutdown=graceful
# 关闭超时
spring.lifecycle.timeout-per-shutdown-phase=20s

#
management.endpoints.web.exposure.include= service-registry
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

这里直接访问 curl "localhost:8771/actuator" 来获取我们注册的这个 API (可以看到我们 - 符号被吃掉了 坑🕳)

接着,我们通过这串请求,改变 Eureka 中服务的状态:DOWN。

curl -X "POST" "http://localhost:8771/actuator/serviceregistry?status=down" -H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"
  • 1.

然后,在等待若干时间后,应该是客户端 consumer 重新去拉取服务列表信息后。(哈哈 我没数)。

不过我配了 10s ,然后我们不断刷新请求,会发现这个负载均衡的效果已经消失了。只剩新的 provider 提供的服务了。

然后在服务稳定一段时间后,可以通过 Prometheus 来观察这个旧的 provider 的 qps 等,当它已经没有啥流量进入了,便可以直接关闭下线了。( kill -9)

上线双订阅客户端

接着,上线这个 新的 consumer ,这里也没啥特别的了。

同样等系统稳定后,下线这个旧的客户端 consumer 了。

而且从上面小实验环节中,我们可以知道流量会先来到这个 Nacos 中,确认里面没有这个服务的话,才去这个 Eureka 中查找。所以到这里,这个 Eureka 中的流量就会少了大部分了。

再次上线

到了这里,我们还不能直接关闭这个 Eureka,还得再次上线新版本的只有 Nacos 注册中心的 provider 和 consumer 。

这次的新版要注意这个负载均衡,我们去掉了 Netflix 后,得手动引入 SpringCloud 的 loadbalancer 组件。其他也就删删配置了。

同样的,稳定后,才去下线双订阅客户端 consumer,再下线双注册服务端 Provider,最后才下线这个 Eureka。 

这里通过 Nginx 等去控制流量,将他们打到新的只订阅 Nacos 的 consumer 上,最后等双订阅的 consumer 客户端没啥流量就给它下线了。

接着,在 Nacos 上 ,下线那个双注册的服务,然后再去下线它。

最后就直接关闭 Eureka 了。

这样就完成了这个注册中心的迁移了。

整体流程

这里其实就是上线新版本后,等其稳定,下线旧版本的一个规则。

看着挺繁琐的

关于应用的发布,这里就不多赘述了,网上大把的 蓝绿发布,灰度发布,还有 K8s 的 pod 容器,docker 等环境下的决策。

最后

https://github.com/Java4ye/springcloud-alibaba-demo-4ye

整个demo 我也弄到 GitHub 上啦,新开的坑, 哈哈,后面也会逐步完善的。

觉得不错的话,可以 Star 支持一波哦!

总结

通过本案例,可以快速了解到这个迁移过程中:

  • 这个代码基本都没改!这得益于这个 SpringCloud 的统一服务注册和发现的编程模型。
  • 使用双注册双订阅模型时,要排除掉自动装配的坑,而且在这个模式下,流量基本都跑到 Nacos 这边。
  • 对比下两个注册中心,更能感觉到 Nacos 这么多便利的功能:上下线和服务列表变化的推送机制。
  • 了解到 Springboot 在优雅下线这一块做的变化,谨记不要轻易 kill -9!