自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這個Dubbo注冊中心擴(kuò)展,有點(diǎn)意思!

開發(fā) 前端
今天想和大家聊聊Dubbo源碼中實(shí)現(xiàn)的一個注冊中心擴(kuò)展。它很特殊,也幫我解決了一個困擾已久的問題,剛剛在生產(chǎn)中用了,效果很好,迫不及待想分享給大家。

大家好~我是小樓。其實(shí)這篇文章早就寫好了,本來上班第一天就發(fā)出來,拖到了現(xiàn)在。

是因?yàn)槲野l(fā)燒躺了3天,今天好點(diǎn)上班了,晚上來打最后一瓶點(diǎn)滴。生病真的很痛苦,大家多休息,多鍛煉,保持好的抵抗力~

今天想和大家聊聊Dubbo源碼中實(shí)現(xiàn)的一個注冊中心擴(kuò)展。它很特殊,也幫我解決了一個困擾已久的問題,剛剛在生產(chǎn)中用了,效果很好,迫不及待想分享給大家。

Dubbo的擴(kuò)展性非常靈活,可以無侵入源碼加載自定義擴(kuò)展。能擴(kuò)展協(xié)議、序列化方式、注冊中心、線程池、過濾器、負(fù)載均衡策略、路由策略、動態(tài)代理等等,甚至「擴(kuò)展本身」也可以擴(kuò)展。

在介紹今天的這個注冊中心擴(kuò)展之前,先拋出一個問題,大家思考一下。

如何低成本遷移注冊中心?

有時出于各種目的需要遷移Dubbo的注冊中心,或因?yàn)橛X得Nacos比較香,想從Zookeeper遷移到Nacos,或因前段時間曝出Consul禁止在中國境內(nèi)使用。

遷移注冊中心的方案大致有兩種:

  • 方案一:使用Dubbo提供的多注冊中心能力,Provider先進(jìn)行雙注冊,Consumer逐步遷移消費(fèi)新注冊中心,最后下線老注冊中心。該方案的缺點(diǎn)是修改時有上下游依賴關(guān)系。
  • 方案二:使用一個同步工具把老注冊中心的數(shù)據(jù)同步到新注冊中心,Consumer逐步遷移到新注冊中心,最后下線老注冊中心。同步工具有開源的Nacos-sync,我之前的文章《zookeeper到nacos的遷移實(shí)踐》就提到了這個方案。這個方案的缺點(diǎn)是架構(gòu)變得復(fù)雜,需要解決同步數(shù)據(jù)的順序性、一致性、同步組件的高可用等問題。

Nacos-sync 參考 https://github.com/nacos-group/nacos-sync

我們從「業(yè)務(wù)方成本」和「基礎(chǔ)架構(gòu)成本」兩個角度考慮一下這兩個方案:

業(yè)務(wù)方成本我們以每次業(yè)務(wù)方修改并上線代碼為1個單位,基礎(chǔ)架構(gòu)成本以增加一個新服務(wù)端組件為2個單位,從復(fù)雜度上來說基礎(chǔ)架構(gòu)成本遠(yuǎn)遠(yuǎn)高于業(yè)務(wù)方修改并上線代碼,但這里我們認(rèn)為只是2倍關(guān)系,做過基礎(chǔ)組件開發(fā)的同學(xué)肯定感同身受,推動別人改代碼比自己埋頭寫代碼要難。

我們統(tǒng)計下上述方案中,遷移一對Consumer和Provider總共需要的成本是多少:

  • 方案一:Provider雙注冊+1;Consumer消費(fèi)新注冊中心+1;Provider下線舊注冊中心+1;總成本為3
  • 方案二:同步組件+2;Consumer消費(fèi)新注冊中心+1;Provider下線舊注冊中心+1;總成本為4

有沒有成本更低的方案?

首先我們不考慮引入同步組件,其次Provider和Consumer能否不修改就能解決?我覺得理論上肯定可以解決,因?yàn)镴ava的字節(jié)碼是可以動態(tài)修改的,肯定能達(dá)到這個目的,但這樣的復(fù)雜度和風(fēng)險會非常高。

退一步能否每個應(yīng)用只修改發(fā)布一次就完成遷移?

Dubbo配置多注冊中心可以參考這篇文章《幾個你不知道的dubbo注冊中心細(xì)節(jié)》,你會發(fā)現(xiàn)多注冊中心是通過配置文件配置的,如下

dubbo.registries.zk1.address=zookeeper://127.0.0.1:2181
dubbo.registries.zk2.address=zookeeper://127.0.0.1:2182

只修改一次代碼,就必須把這個配置變成動態(tài)的,有點(diǎn)難,但不是做不到,可在應(yīng)用啟動時遠(yuǎn)程加載配置,或者采取替換配置文件的方式來達(dá)到目的。

但這只解決了部分問題,還有兩個問題需要解決:

  • Dubbo注冊、訂閱都發(fā)生在應(yīng)用啟動時,應(yīng)用啟動后就沒法修改了。也不是完全不能,如果采用了api的方式接入Dubbo可以通過改代碼來實(shí)現(xiàn),但幾乎這種方式不會被采用;
  • Dubbo消費(fèi)沒法動態(tài)切換,多注冊中心消費(fèi)時,Dubbo默認(rèn)的行為是挑第一個可用的注冊中心進(jìn)行調(diào)用,無法主動地進(jìn)行切換;如果實(shí)現(xiàn)了主動切換還有個好處是穩(wěn)定性提高了很多,萬一新注冊中心出現(xiàn)問題還可以及時切回去。

這里針對第二點(diǎn)的消費(fèi)邏輯做一點(diǎn)簡單說明,老版本(<2.7.5)邏輯比較簡單粗暴,代碼位于RegistryAwareClusterInvoker:

  • 挑選第一個可用的注冊中心進(jìn)行調(diào)用
  • 新版本(>=2.7.5)則稍微豐富一點(diǎn),代碼位于ZoneAwareClusterInvoker:
  • 挑選一個可用且?guī)referred偏好配置的注冊中心進(jìn)行調(diào)用,注意這個偏好配置不同版本key還不一樣,有點(diǎn)坑
  • 如果都不符合1,則挑選同一個分區(qū)且可用的注冊中心進(jìn)行調(diào)用,分區(qū)也是通過參數(shù)配置,這個主要是為了跨機(jī)房的就近訪問

如果1、2都不符合,通過一個負(fù)載均衡算法挑選出一個可用的注冊中心進(jìn)行調(diào)用

如果1、2、3都不符合,則挑選一個可用的注冊中心

如果上述都不符合,則使用第一個注冊中心進(jìn)行調(diào)用

可以看出新版本功能很豐富,但它是有版本要求的,而且控制的key也變來變?nèi)ィ踔寥ニ岩幌乱灿蠦ug存在,所以如果是單一穩(wěn)定的高版本是可以通過這個來做,但大部分還是達(dá)不到這個要求。

很長一段時間以來,我都沒有想到一個好的辦法來解決這個問題,甚至我們公司內(nèi)部有直接修改Dubbo源碼來實(shí)現(xiàn)動態(tài)切換消費(fèi)的能力,但這種入侵修改無法持續(xù),直到有一天瀏覽Dubbo源碼時,無意間看到了MultipleRegistry,仿佛發(fā)現(xiàn)了新大陸,用醍醐灌頂來形容一點(diǎn)不為過。

MultipleRegistry,有點(diǎn)意思!

MultipleRegistry是Dubbo 2.7.2引入的一個注冊中心擴(kuò)展,注冊中心擴(kuò)展圈起來,要考!意味著這個擴(kuò)展可以在任何>=2.7.0版本上運(yùn)行,稍微改改也能在2.7以下的版本使用

這究竟是個什么注冊中心的擴(kuò)展呢?

實(shí)際上這個擴(kuò)展并不是一個實(shí)際的注冊中心的擴(kuò)展,而是一個包裝,它本身不提供服務(wù)注冊發(fā)現(xiàn)的能力,它只是把其他注冊中心聚合起來的一個空殼。

為什么這個「空殼」這么厲害呢?下面我們就來分析分析源碼。

由于剛好手上有3.0.0版本的源碼,所以接下來的源碼分析基于Dubbo 3.0.0版本,也不用擔(dān)心版本問題,這個擴(kuò)展自從2.7.2引入之后幾乎沒有大的改動,只有Bugfix,所以什么版本基本都差不多。只分析接口級服務(wù)發(fā)現(xiàn),應(yīng)用級的暫時不分析,原理類似。

不過在講源碼之前,還得說說Dubbo注冊中心插件的運(yùn)行原理,否則源碼可能看不懂,我們以開發(fā)一個注冊中心擴(kuò)展為例:

Dubbo注冊中心擴(kuò)展需實(shí)現(xiàn)RegistryService和RegistryFactory接口


public interface RegistryService {

void register(URL url);

void unregister(URL url);

void subscribe(URL url, NotifyListener listener);

void unsubscribe(URL url, NotifyListener listener);

List<URL> lookup(URL url);
}

這里的五個接口分別是注冊、注銷、訂閱、取消訂閱、查詢,在Dubbo應(yīng)用啟動時會調(diào)用這些接口。

都比較好理解,需要提一下subscribe接口。

subscribe傳入了一個NotifyListener參數(shù),可以理解為一個回調(diào),當(dāng)監(jiān)聽的的URL發(fā)生變化時,調(diào)用這個NotifyListener通知Dubbo。

public interface NotifyListener {
void notify(List<URL> urls);
}

NotifyListener也是個接口,只有一個notify方法,這個方法傳入的參數(shù)是所消費(fèi)的URL的所有Provider列表。

@SPI("dubbo")
public interface RegistryFactory {
@Adaptive({"protocol"})
Registry getRegistry(URL url);
}

RegistryFactory是描述了如何創(chuàng)建Registry擴(kuò)展的工廠類,URL就是配置中

zookeeper://127.0.0.1:2181

還需要遵守Dubbo SPI的加載規(guī)則擴(kuò)展才能被正確加載

這些內(nèi)容官方文檔中說的比較清楚,如果有疑問可以看看Dubbo的官方文檔說明。

簡單介紹到此結(jié)束,接下來重點(diǎn)介紹MultipleRegistry。

首先看初始化,代碼只挑出重點(diǎn),在初始化MultipleRegistry時,分別對注冊和訂閱的注冊中心進(jìn)行初始化,這些注冊中心來自MultipleRegistry的URL配置,URL上的key分別為service-registry、reference-registry,實(shí)際測試下來URL的參數(shù)中帶奇怪的字符會導(dǎo)致編譯不通過,不過這并不是重點(diǎn),基本的還是可用,而且也不一定要采用這種配置。

public MultipleRegistry(URL url, boolean initServiceRegistry, boolean initReferenceRegistry) {
...
Map<String, Registry> registryMap = new HashMap<>();
// 初始化注冊的注冊中心
if (initServiceRegistry) {
initServiceRegistry(url, registryMap);
}
// 初始化訂閱的注冊中心
if (initReferenceRegistry) {
initReferenceRegistry(url, registryMap);
}
...
}

我們再看注冊和訂閱:

注冊比較簡單,只需要對剛剛初始化的serviceRegistries都進(jìn)行注冊即可

 public void register(URL url) {
super.register(url);
for (Registry registry : serviceRegistries.values()) {
registry.register(url);
}
}

訂閱時也是針對referenceRegistries的每個注冊中心都訂閱,但這里有個不同的點(diǎn)是NotifyListener的妙用。

 public void subscribe(URL url, NotifyListener listener) {
MultipleNotifyListenerWrapper multipleNotifyListenerWrapper = new MultipleNotifyListenerWrapper(listener);
multipleNotifyListenerMap.put(listener, multipleNotifyListenerWrapper);
for (Registry registry : referenceRegistries.values()) {
SingleNotifyListener singleNotifyListener = new SingleNotifyListener(multipleNotifyListenerWrapper, registry);
multipleNotifyListenerWrapper.putRegistryMap(registry.getUrl(), singleNotifyListener);
registry.subscribe(url, singleNotifyListener);
}
super.subscribe(url, multipleNotifyListenerWrapper);
}

先用MultipleNotifyListenerWrapper把最原始的NotifyListener包裝起來,NotifyListener傳給每個被包裝的注冊中心。MultipleNotifyListenerWrapper和SingleNotifyListener分別是什么?

MultipleNotifyListenerWrapper將原始的NotifyListener進(jìn)行包裝,且持有SingleNotifyListener的引用,它提供了一個方法notifySourceListener的方法,將持有的SingleNotifyListener中上次變更的URL列表進(jìn)行merge后調(diào)用最原始的NotifyListener.notify()

protected class MultipleNotifyListenerWrapper implements NotifyListener {

Map<URL, SingleNotifyListener> registryMap = new ConcurrentHashMap<URL, SingleNotifyListener>(4);
NotifyListener sourceNotifyListener;
...

public synchronized void notifySourceListener() {
List<URL> notifyURLs = new ArrayList<URL>();
URL emptyURL = null;
for (SingleNotifyListener singleNotifyListener : registryMap.values()) {
List<URL> tmpUrls = singleNotifyListener.getUrlList();
if (CollectionUtils.isEmpty(tmpUrls)) {
continue;
}
// empty protocol
if (tmpUrls.size() == 1
&& tmpUrls.get(0) != null
&& EMPTY_PROTOCOL.equals(tmpUrls.get(0).getProtocol())) {
// if only one empty
if (emptyURL == null) {
emptyURL = tmpUrls.get(0);
}
continue;
}
notifyURLs.addAll(tmpUrls);
}
// if no notify URL, add empty protocol URL
if (emptyURL != null && notifyURLs.isEmpty()) {
notifyURLs.add(emptyURL);
}
this.notify(notifyURLs);
}
...
}

再看SingleNotifyListener,它的notify去調(diào)用MultipleNotifyListenerWrapper的notifySourceListener

class SingleNotifyListener implements NotifyListener {

MultipleNotifyListenerWrapper multipleNotifyListenerWrapper;
Registry registry;
volatile List<URL> urlList;

@Override
public synchronized void notify(List<URL> urls) {
this.urlList = urls;
if (multipleNotifyListenerWrapper != null) {
this.multipleNotifyListenerWrapper.notifySourceListener();
}
}
...
}

仔細(xì)思考我們發(fā)現(xiàn):

  • MultipleNotifyListenerWrapper是個注冊中心擴(kuò)展的包裝,它本身是沒有通知能力的,只能借助的真實(shí)注冊中心擴(kuò)展的通知能力
  • SingleNotifyListener是真實(shí)的注冊中心的通知回調(diào),由它去調(diào)用MultipleNotifyListenerWrapper的notifySourceListener,調(diào)用前可將數(shù)據(jù)進(jìn)行merge

如果你仔細(xì)讀完上面的文章你會發(fā)現(xiàn),這不就是包裝了一下注冊中心擴(kuò)展嗎?就這?哪里醍醐灌頂了?

不著急,我們先扒一扒作者為什么寫這樣一個擴(kuò)展,他的初衷是想解決什么問題?

參考這個issue:https://github.com/apache/dubbo/issues/3932

作者說:我們可以在程序運(yùn)行時下線(注銷)服務(wù),如果有個Dubbo服務(wù)同時注冊了Zookeeper和Nacos,而我只想注銷其中一個注冊中心,MultipleRegistry就可以解決這種場景。

作者的初衷很簡單,但當(dāng)我看到這個實(shí)現(xiàn)時,靈光乍現(xiàn),感覺這個實(shí)現(xiàn)如果稍微改一改,簡直就是一個Dubbo多注冊中心遷移神器。

Dubbo多注冊中心遷移神器

Dubbo多注冊中心遷移神器具有什么樣的特性?

  • 可以動態(tài)(遠(yuǎn)程配置)地注冊到一個或多個注冊中心,且在程序不重啟的情況下可以動態(tài)調(diào)整
  • 可以動態(tài)(遠(yuǎn)程配置)地消費(fèi)某一個或多個注冊中心,同樣可以在程序不重啟的情況下可以動態(tài)調(diào)整
  • 消費(fèi)有兜底邏輯,比如配置了消費(fèi)Zookeeper,但Zookeeper上可能只有A服務(wù),B服務(wù)不存在,那么調(diào)用B服務(wù)時可以用其他注冊中心的Provider來兜底,這就保證了注冊中心遷移過程中沒用上下游的依賴

如果上面說的能夠領(lǐng)會到,這些需求實(shí)現(xiàn)起來就很簡單:

  • 啟動時,Provider和Consumer都分別監(jiān)聽對應(yīng)的配置項,按需注冊和消費(fèi),目前MultipleRegistry已經(jīng)實(shí)現(xiàn)
  • Dubbo應(yīng)用運(yùn)行中,配置項變更事件驅(qū)動
  • Provider:觸發(fā)一個重新注冊、注銷的事件,根據(jù)最新的配置項將需要注冊的注冊中心再注冊一遍,需要注銷的注冊中心注銷
  • Consumer:觸發(fā)重新進(jìn)行訂閱和取消訂閱,
  • 消費(fèi)兜底邏輯,將MultipleNotifyListenerWrapper中的notifySourceListener的merge邏輯進(jìn)行重寫,可以實(shí)現(xiàn)有線消費(fèi)、無對應(yīng)Provider兜底消費(fèi)。當(dāng)然如果配置變更也需要觸發(fā)一次notify

按照這個思路,我已經(jīng)實(shí)現(xiàn)了一個版本在線上跑了起來!不過耦合了公司內(nèi)部的配置中心。

如果想不耦合,可以采用Dubbo SPI擴(kuò)展的方式來擴(kuò)展「讀取監(jiān)聽配置變更部分」,擴(kuò)展中的擴(kuò)展,有點(diǎn)騷~

這篇文章有點(diǎn)長,最后來回顧一下講了啥:

首先文章從一個Dubbo注冊中心遷移成本的問題講起,現(xiàn)有的方案成本都是比較高,一直苦苦找尋更低成本、兼容性更強(qiáng)的方案。終于在一次瀏覽Dubbo源碼過程中發(fā)現(xiàn)了MultipleRegistry源碼,經(jīng)過研究發(fā)現(xiàn)只需要經(jīng)過稍微的修改就能符合我們對完美動態(tài)注冊中心的定義。

在我寫這篇文章的時候,又試圖搜索了一下Dubbo動態(tài)注冊中心,發(fā)現(xiàn)了「Kirito的技術(shù)分享」的一篇文章《平滑遷移 Dubbo 服務(wù)的思考》提到了阿里云的一個產(chǎn)品的實(shí)現(xiàn)和上文提到的方案類似。

如果剛好你也有這個需求,可以用上文的思路實(shí)現(xiàn)看看,并不復(fù)雜,是不是感覺賺了一個億。


責(zé)任編輯:武曉燕 來源: 捉蟲大師
相關(guān)推薦

2022-04-27 20:02:22

Dubbo注冊中心開發(fā)

2023-01-30 22:43:39

DubboZooKeeper

2024-04-10 12:22:19

DubboNacos微服務(wù)

2021-03-09 07:37:41

DHCP協(xié)議地址

2022-02-10 08:07:45

DubboRPC框架

2014-09-17 14:05:14

2016-08-16 11:53:37

2021-06-07 17:34:39

拓?fù)渑判?/a>排序算法數(shù)據(jù)結(jié)構(gòu)與算法

2013-01-06 12:57:23

2021-03-09 08:00:13

設(shè)計秒殺TPS

2013-09-17 10:04:36

大數(shù)據(jù)西大荒

2013-09-16 11:04:23

大數(shù)據(jù)大數(shù)據(jù)發(fā)展

2022-01-28 09:01:49

架構(gòu)

2021-08-12 23:03:21

5G運(yùn)營商基站

2020-12-24 08:37:41

Css前端加載動畫

2024-02-23 09:02:21

前端開源項目

2022-10-19 08:01:17

Gif動圖前端

2023-12-21 08:02:31

React DnD拖拽庫組件

2023-04-12 08:00:34

Dubbo分布式服務(wù)

2021-05-27 11:10:23

注冊中心業(yè)務(wù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號