遷移 Eureka 到 Nacos 之雙注冊雙訂閱模式
這里面涉及到這個 雙注冊雙訂閱模式 ,下面讓我們一起看看吧!
內容概覽
首先,為啥要遷移呢?
主要是它對比其他注冊中心,已經(jīng)落后太多了。
- 就拿 Nacos 來說吧,不僅有 配置中心,管理界面,還能手動上下線,而且支持服務列表變更的消息推送模式(實時性高)。
- Eureka 1.x 的架構有些地方可以改進,比如 在客戶端的 pull 模式下,增加這個消息推送模式,增加實時性;還有 集群,Eureka 只支持 AP ,各個客戶端都能進行寫請求 , 沒有主從節(jié)點之分,各個節(jié)點之間通過相互復制來同步數(shù)據(jù),無法保證一致性。Nacos 則有 AP 和 CP 兩種選擇,更靈活。
- 2019年的某個會上,Spring 團隊提出如何解決 Netflix 進入維護模式后的 SpringCLoud 組件選擇問題。
- 就是 Eureka 早已進入維護模式啦!而且 long long ago ,官方就放棄了這個 Eureka2.X 版本的開發(fā)(看了下分支,6,7年前的代碼了),而且官方還說了不能用在生產(chǎn)上,后果自負(咱也不知道它有啥新特點,反正 SpringCloud 一直用的 1.x 版本。現(xiàn)在更新到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 時會出現(xiàn)另一個,同樣排除掉即可)
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]
可以看到是自動裝配時,不知道用哪個導致的異常。但是我們兩個都要。
這里只要在 application.properties 中把這個自動裝配移除掉即可。
# 雙注冊模式下關閉
spring.autoconfigure.exclude=org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration,org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration
當然,秉著嚴謹?shù)膽B(tài)度,我們在這兩個類中打入相應的斷點,可以看到他們都被創(chuàng)建了。
同時,也成功注冊到這兩個注冊中心去了。
Eureka
Nacos
這個時候,再次訪問舊的客戶端 8772 端口的,可以發(fā)現(xiàn)如下效果。
但是要注意,此時項目的架構變成這樣,consumer 中只有 Eureka 的客戶端,所以調用到的都是 Eureka 中心中的服務。此時流量不會走到 Nacos 這邊
接著便是看客戶端 consumer 正不正常,比如跑個一天看看。
穩(wěn)定后,下一步就是 下線這個 provider ,然后看看正不正常了。同樣穩(wěn)定后,便是準備啟動這個 雙訂閱的客戶端了。
小實驗
但是我這里做了一個小實驗 哈哈 想看看不下線的情況,我這個 新客戶端 上線后是使用哪個注冊中心的服務多點。
所以,接著,我們就啟動這個新的 consumer,一個雙訂閱的客戶端。
同樣修改下配置文件即可。
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服務發(fā)現(xiàn)與注冊配置,其中子屬性 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
這個時候,我們訪問新的客戶端。8872 端口的:http://localhost:8872/hello/Java4ye
發(fā)現(xiàn)無論怎么刷新,接口的返回值都是下面這個,無法達到負載均衡的效果 。(⊙o⊙)?
簡單翻看了下源碼,可以發(fā)現(xiàn)系統(tǒng)創(chuàng)建了三個 discoveryClient ,最后一個是兜底用的。
而且 nacos 排在第一個,這意味著從 nacos 的注冊中心中找到服務的話,就不會調用到 Eureka 中的了。
了解了這個原理后,將 nacos 中的服務進行下線。
然后去刷新新的客戶端,8872 端口的,可以發(fā)現(xiàn),又出現(xiàn)了負載均衡的效果了。
而且得益于 Nacos 的服務列表變更推送機制,我們客戶端可以實時感知到 服務列表的 變化,這個時候直接去刷新新客戶端的接口,可以發(fā)現(xiàn)它已經(jīng)切換到 Eureka 中了,沒有延遲感!
所以當我們在遷移的過程中,如果發(fā)現(xiàn) Nacso 上新的 provider 有什么異常時,可以將其下線先?? 輕輕一點真的太方便了。
優(yōu)雅下線
結束上面的小實驗,回到正常流程中,我們要來下線這個 provider 了。
階段目標
這里就得考慮這個 優(yōu)雅下線 的問題了。
網(wǎng)上的方案很多,這里用 Springboot 的 actuator 來實現(xiàn)。
這個 graceful 配置是 Springboot2.3 之后才有的,會讓內嵌服務器拒絕外部請求,然后處理完已經(jīng)在內部的請求后,進入關閉狀態(tài)。
通過這個暴露的 api,去修改 eureka 中 service 的狀態(tài)。
# 優(yōu)雅下線
server.shutdown=graceful
# 關閉超時
spring.lifecycle.timeout-per-shutdown-phase=20s
#
management.endpoints.web.exposure.include= service-registry
這里直接訪問 curl "localhost:8771/actuator" 來獲取我們注冊的這個 API (可以看到我們 - 符號被吃掉了 坑??)
接著,我們通過這串請求,改變 Eureka 中服務的狀態(tài):DOWN。
curl -X "POST" "http://localhost:8771/actuator/serviceregistry?status=down" -H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"
然后,在等待若干時間后,應該是客戶端 consumer 重新去拉取服務列表信息后。(哈哈 我沒數(shù))。
不過我配了 10s ,然后我們不斷刷新請求,會發(fā)現(xiàn)這個負載均衡的效果已經(jīng)消失了。只剩新的 provider 提供的服務了。
然后在服務穩(wěn)定一段時間后,可以通過 Prometheus 來觀察這個舊的 provider 的 qps 等,當它已經(jīng)沒有啥流量進入了,便可以直接關閉下線了。( kill -9)
上線雙訂閱客戶端
接著,上線這個 新的 consumer ,這里也沒啥特別的了。
同樣等系統(tǒng)穩(wěn)定后,下線這個舊的客戶端 consumer 了。
而且從上面小實驗環(huán)節(jié)中,我們可以知道流量會先來到這個 Nacos 中,確認里面沒有這個服務的話,才去這個 Eureka 中查找。所以到這里,這個 Eureka 中的流量就會少了大部分了。
再次上線
到了這里,我們還不能直接關閉這個 Eureka,還得再次上線新版本的只有 Nacos 注冊中心的 provider 和 consumer 。
這次的新版要注意這個負載均衡,我們去掉了 Netflix 后,得手動引入 SpringCloud 的 loadbalancer 組件。其他也就刪刪配置了。
同樣的,穩(wěn)定后,才去下線雙訂閱客戶端 consumer,再下線雙注冊服務端 Provider,最后才下線這個 Eureka。
這里通過 Nginx 等去控制流量,將他們打到新的只訂閱 Nacos 的 consumer 上,最后等雙訂閱的 consumer 客戶端沒啥流量就給它下線了。
接著,在 Nacos 上 ,下線那個雙注冊的服務,然后再去下線它。
最后就直接關閉 Eureka 了。
這樣就完成了這個注冊中心的遷移了。
整體流程
這里其實就是上線新版本后,等其穩(wěn)定,下線舊版本的一個規(guī)則。
看著挺繁瑣的
關于應用的發(fā)布,這里就不多贅述了,網(wǎng)上大把的 藍綠發(fā)布,灰度發(fā)布,還有 K8s 的 pod 容器,docker 等環(huán)境下的決策。
最后
https://github.com/Java4ye/springcloud-alibaba-demo-4ye
整個demo 我也弄到 GitHub 上啦,新開的坑, 哈哈,后面也會逐步完善的。
覺得不錯的話,可以 Star 支持一波哦!
總結
通過本案例,可以快速了解到這個遷移過程中:
- 這個代碼基本都沒改!這得益于這個 SpringCloud 的統(tǒng)一服務注冊和發(fā)現(xiàn)的編程模型。
- 使用雙注冊雙訂閱模型時,要排除掉自動裝配的坑,而且在這個模式下,流量基本都跑到 Nacos 這邊。
- 對比下兩個注冊中心,更能感覺到 Nacos 這么多便利的功能:上下線和服務列表變化的推送機制。
- 了解到 Springboot 在優(yōu)雅下線這一塊做的變化,謹記不要輕易 kill -9!