不一樣的dubbo原理講解(分享)

轉(zhuǎn)自:https://blog.csdn.net/manzhizhen/article/details/53025666
我們知道俺猿,對于服務(wù)治理框架來說,服務(wù)通信(RPC)和服務(wù)管理兩部分必不可少,而服務(wù)管理又分為服務(wù)注冊欠橘、服務(wù)發(fā)現(xiàn)和服務(wù)人工介入涛目,我們來看看Dubbo框架的結(jié)構(gòu)圖

image.png

圖中可以看出,服務(wù)提供者Provider往服務(wù)注冊中心Registry注冊服務(wù)靠抑,而的消費(fèi)者Consumer從服務(wù)注冊中心訂閱它需要的服務(wù)量九,而不是全部服務(wù),當(dāng)有新的Provider出現(xiàn)颂碧,或者現(xiàn)有Provider宕機(jī)荠列,注冊中心Registry都應(yīng)該能盡早發(fā)現(xiàn),并將新的Provider列表推送給對應(yīng)的Consumer载城,有了這樣的機(jī)制肌似,Dubbo才能做到Failover,而Failover的時(shí)效性诉瓦,由注冊中心Registry的實(shí)現(xiàn)決定川队。

    Dubbo線上支持三種注冊中心:自帶的SimpleRegistry、Redis和Zookeeper睬澡,當(dāng)然固额,最常用的還是Zookeeper作為注冊中心,因?yàn)樘喾植际降闹虚g件需要依賴Zookeeper作為協(xié)作者猴贰。那么怎么才能讓Dubbo知道我們使用哪個(gè)實(shí)現(xiàn)作為注冊中心呢对雪?我們只需要在dubbo的xml配置文件中配置dubbo:registry節(jié)點(diǎn)即可:

<dubbo:registry id="dubboRegistry"protocol="zookeeper"address="${dubbo.registry.address}"/>

沒錯(cuò),protocol就指明了注冊中心的實(shí)現(xiàn)米绕。

    要想做到服務(wù)的可靠瑟捣,避免分布式系統(tǒng)的單點(diǎn)問題近迁,除了Provider可以集群部署外拴疤,注冊中心的弱依賴也是必須的碍讨,注冊中心的宕機(jī)拓诸,不會(huì)影響現(xiàn)有服務(wù)的運(yùn)行,只是不能注冊新的服務(wù)和進(jìn)行服務(wù)發(fā)現(xiàn)桑李,F(xiàn)ailover還是可以做的踱蛀,比如Consumer可以通過服務(wù)調(diào)用來簡單判斷當(dāng)前的Provier是否可用。如果某個(gè)Consumer宕機(jī)了贵白,當(dāng)它重啟后率拒,發(fā)現(xiàn)注冊中心也掛了,那咋辦禁荒?為了防止這種問題出現(xiàn)猬膨,Dubbo的Consumer會(huì)將自己需要的Provider列表在本地保存一份,當(dāng)然呛伴,里面也包括自己暴露的服務(wù)信息(即自己也作為Provider)勃痴,我們可以看看AbstractRegistry中的實(shí)現(xiàn):

public AbstractRegistry(URL url) {

setUrl(url);

// 啟動(dòng)文件保存定時(shí)器

syncSaveFile= url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY,false);

String filename =url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-"+ url.getHost() +".cache");

File file = null;

if (ConfigUtils.isNotEmpty(filename)) {

   file = newFile(filename);

   if(! file.exists() &&file.getParentFile() !=null&&! file.getParentFile().exists()){

       if(! file.getParentFile().mkdirs()){

            throw new IllegalArgumentException("Invalid registry store file "+ file +", cause: Failed to create directory" + file.getParentFile()+"!");

        }

    }

}

this.file= file;

loadProperties();

notify(url.getBackupUrls());

}

注意看黃底代碼部分,如果沒有在屬性文件中配置file(Constants.FILE_KEY),就將在用戶的當(dāng)前用戶目錄/.dubbo/目錄下新建一個(gè)dubbo-registry開頭的保存所有URL信息的Cache文件热康,通常來說一個(gè)應(yīng)用可以在多個(gè)注冊中心暴露自己的服務(wù)沛申,也可以從多個(gè)注冊中心訂閱不同的服務(wù),所以這里的Cache文件名加入了注冊中心的主機(jī)名姐军。還有一個(gè)lock文件铁材,用來防止不同的JVM進(jìn)程同時(shí)修改Cache文件,注意庶弃,這里只是防止衫贬,所以意味著同一目錄的Cache文件可以由多個(gè)JVM進(jìn)程共享德澈,當(dāng)多個(gè)JVM進(jìn)程恰巧同時(shí)修改Cache文件時(shí)歇攻,將會(huì)有一個(gè)進(jìn)程獲取lock文件的鎖失敗,見保存Cache的過程的AbstractRegistry#doSaveProperties方法的片段:

FileChannel channel = raf.getChannel();

try {

FileLocklock = channel.tryLock();

if (lock == null) {

   thrownew IOException("Can not lock theregistry cache file "+file.getAbsolutePath() + ", ignore and retrylater, maybe multi java process use the file, please config:dubbo.registry.file=xxx.properties");

}

這將導(dǎo)致某個(gè)URL更新到Cache文件失敗梆造,但Dubbo提供了重試機(jī)制缴守,以保證Cache文件中信息能和內(nèi)存中的信息最終一致。但不要認(rèn)為Cache文件中的Provider和Consumer列表是和當(dāng)前運(yùn)行的服務(wù)一致镇辉,因?yàn)楫?dāng)一個(gè)服務(wù)部署多個(gè)應(yīng)用時(shí)屡穗,Cache文件被多個(gè)JVM同時(shí)寫的概率還是很大的,所以這時(shí)總有JVM進(jìn)程度lock文件獲取鎖失敽龈亍(即FileChannel#tryLock()失敶迳啊),這時(shí)它只能乖乖稍后重試了屹逛。寫Cache的方式也很簡單粗暴础废,即先讀取整個(gè)Cache文件汛骂,然后再往其寫入當(dāng)前處理的URL,然后再全量寫入评腺,可見帘瞭,如果某個(gè)服務(wù)(URL)已經(jīng)不再使用,它有可能一直存在于Cache文件中蒿讥。

     保存Cache還分為同步保存和異步保存蝶念,我們知道內(nèi)存中服務(wù)列表的更新相對于服務(wù)調(diào)用來說肯定是異步的,但為啥保存Cache文件還要分同步和異步呢芋绸?因?yàn)樵贒ubbo中媒殉,服務(wù)(或者叫URL)是一個(gè)個(gè)來更新的,也就是說摔敛,當(dāng)服務(wù)比較多時(shí)适袜,使用異步保存Cache文件能使應(yīng)用啟動(dòng)和服務(wù)更新速度更快,而整個(gè)更新過程是由AbstractRegistry#notify來觸發(fā)的舷夺。

  我們再來看看如果選擇使用Zookeeper用來做Dubbo的注冊中心苦酱,那么Provider和Consumer的數(shù)據(jù)在上是怎么存儲(chǔ)的。Dubbo在ZK的所有數(shù)據(jù)都在/dubbo節(jié)點(diǎn)下给猾,如下圖:

/dubbo

/com.manzhizhen.user.Service1

/consumers

/routers

/providers

/configurators

/com.manzhizhen.user.Service2

/consumers

/routers

/providers

/configurators

/com.manzhizhen.user.Service3

/consumers

/routers

/providers

/configurators

我們可以看到疫萤,每個(gè)服務(wù)(URL)在dubbo節(jié)點(diǎn)下都會(huì)有一個(gè)對應(yīng)的ZK持久化節(jié)點(diǎn),而每個(gè)服務(wù)節(jié)點(diǎn)下面都會(huì)有四個(gè)持久化子節(jié)點(diǎn)敢伸,代表消費(fèi)者(consumer)扯饶、路由(routers)、提供者(providers)和配置(configurators)池颈,consumer和providers節(jié)點(diǎn)好理解尾序,放的就是該URL下消費(fèi)者和提供者的URL全部信息,而routers和configurators主要用于控制路由規(guī)則躯砰,這在正常情況下是用的比較少的每币,所以這兩個(gè)節(jié)點(diǎn)數(shù)據(jù)通常為空。

 現(xiàn)在我們說說和服務(wù)注冊相關(guān)的兩個(gè)異常信息琢歇, 先給出Dubbo的集群容錯(cuò)圖:
image.png

一個(gè)常見的異常信息是"Forbid consumer XXXXXaccess service XXXXX from registry XXXXX use dubbo version 2.5.3, Please checkregistry access list (whitelist/blacklist)."兰怠,當(dāng)我們需要調(diào)用服務(wù)時(shí),會(huì)先從本地的注冊目錄也就是RegistryDirectory來拿取調(diào)用(Invoker)列表李茫,見上圖Directory節(jié)點(diǎn)揭保,RegistryDirectory#doList代碼片段如下:

public List<Invoker<T>> doList(Invocation invocation) {

if (forbidden) {

   thrownew RpcException(RpcException.FORBIDDEN_EXCEPTION,"Forbid consumer "+ NetUtils.getLocalHost() + " access service" +getInterface().getName() + " from registry "+ getUrl().getAddress() +" use dubbo version " + Version.getVersion() + ", Please check registry access list(whitelist/blacklist).");

}

List<Invoker<T>> invokers = null;

Map<String, List<Invoker<T>>>localMethodInvokerMap =this.methodInvokerMap;// local reference

可見,當(dāng)forbidden為false時(shí)魄宏,會(huì)拋出該異常信息秸侣,當(dāng)注冊中心給它推送最新的Provider列表時(shí),上面的forbidden的值已經(jīng)變成了false,見RegistryDirectory#refreshInvoker代碼片段:

private void refreshInvoker(List<URL>invokerUrls){

if(invokerUrls !=null&&invokerUrls.size() ==1&& invokerUrls.get(0) !=null

        && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {

    this.forbidden=true;//禁止訪問

    this.methodInvokerMap=null; // 置空列表

    destroyAllInvokers(); // 關(guān)閉所有Invoker

} else{

   this.forbidden=false;//允許訪問

    Map<String, Invoker<T>>oldUrlInvokerMap =this.urlInvokerMap;// local reference

    if (invokerUrls.size() ==0&&this.cachedInvokerUrls!=null){

       invokerUrls.addAll(this.cachedInvokerUrls);

   } else{

       this.cachedInvokerUrls=new HashSet<URL>();

        this.cachedInvokerUrls.addAll(invokerUrls);//緩存invokerUrls列表味榛,便于交叉對比

    }

從上面代碼可以看出方篮,當(dāng)該URL協(xié)議為empty時(shí),說明該URL已經(jīng)被禁止(forbidden)了励负,那什么時(shí)候URL的協(xié)議會(huì)被設(shè)置成empty呢藕溅?我們看看ZookeeperRegistry#toUrlsWithEmpty方法:

private List<URL> toUrlsWithEmpty(URLconsumer, String path, List<String> providers) {

List<URL> urls = toUrlsWithoutEmpty(consumer, providers);

if (urls == null || urls.isEmpty()) {

  int i = path.lastIndexOf('/');

  String category = i < 0? path : path.substring(i +1);

  URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);

   urls.add(empty);

}

return urls;

}

可見,當(dāng)providers列表為空時(shí)继榆,也就是某個(gè)URL下沒有活著的Provider時(shí)巾表,Consumer會(huì)將本地的invokerUrl的協(xié)議設(shè)置成empty,而toUrlsWithEmpty是在ZookeeperRegistry訂閱方法doSubscribe中被調(diào)用的略吨,這里不再給出代碼集币。

另一個(gè)是"Failed to invoke the method XXXXXin the service XXXXX. No provider available for the service XXXXX from registryXXXXX on the consumer XXXXX using the dubbo version 2.5.3. Please check if theproviders have been started and registered.",因?yàn)槊看握{(diào)用時(shí)都會(huì)去檢查調(diào)用列表翠忠,如果列表有多個(gè)可用服務(wù)(即多個(gè)Provider)鞠苟,將會(huì)使用配置的負(fù)載均衡方式來選擇一個(gè)服務(wù)來調(diào)用,但如果服務(wù)列表為空秽之,則會(huì)拋異常当娱,也就是在上圖的Invoker節(jié)點(diǎn)拋出異常,這種情況一般是說明當(dāng)前沒有可用的Provider考榨,見AbstractClusterInvoker#checkInvokers代碼:

protected void checkInvokers(List<Invoker<T>> invokers, Invocation invocation) {

if (invokers == null|| invokers.size() ==0) {

   thrownew RpcException("Failed to invokethe method "

            + invocation.getMethodName() +" in the service "+ getInterface().getName()

            + ". No provideravailable for the service "+directory.getUrl().getServiceKey()

            + " from registry" + directory.getUrl().getAddress()

            + " on the consumer" + NetUtils.getLocalHost()

            + " using the dubboversion " + Version.getVersion()

            + ". Please check ifthe providers have been started and registered.");

}

}

對于這兩個(gè)異常的直接結(jié)論是跨细,如果某個(gè)URL去注冊中心注冊過,但后來該URL下沒有Provider了河质,那么此時(shí)Consumer調(diào)用Provider將報(bào)第一種異常冀惭;如果Consumer調(diào)用了一個(gè)從未去注冊中心注冊過的URL,則會(huì)報(bào)第二種異常掀鹅。

需要明確一點(diǎn)的是散休,注冊中心的兩個(gè)重要目的是服務(wù)發(fā)現(xiàn)和服務(wù)人工介入,線上的Provider和Consumer都不能強(qiáng)依賴注冊中心乐尊,哪怕注冊中心是雙機(jī)部署戚丸,但要做到對注冊中心的弱依賴,Consumer端需要有簡單的負(fù)載均衡和Failover機(jī)制科吭。
文末附上dubbo調(diào)用邏輯


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昏滴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子对人,更是在濱河造成了極大的恐慌,老刑警劉巖拂共,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牺弄,死亡現(xiàn)場離奇詭異,居然都是意外死亡宜狐,警方通過查閱死者的電腦和手機(jī)势告,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蛇捌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咱台,你說我怎么就攤上這事络拌。” “怎么了回溺?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵春贸,是天一觀的道長。 經(jīng)常有香客問我遗遵,道長萍恕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任车要,我火速辦了婚禮允粤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翼岁。我一直安慰自己类垫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布琅坡。 她就那樣靜靜地躺著阔挠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脑蠕。 梳的紋絲不亂的頭發(fā)上购撼,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音谴仙,去河邊找鬼迂求。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晃跺,可吹牛的內(nèi)容都是我干的揩局。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼掀虎,長吁一口氣:“原來是場噩夢啊……” “哼凌盯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烹玉,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驰怎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后二打,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體县忌,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了症杏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片装获。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厉颤,靈堂內(nèi)的尸體忽然破棺而出穴豫,到底是詐尸還是另有隱情,我是刑警寧澤逼友,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布精肃,位于F島的核電站,受9級特大地震影響翁逞,放射性物質(zhì)發(fā)生泄漏肋杖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一挖函、第九天 我趴在偏房一處隱蔽的房頂上張望状植。 院中可真熱鬧,春花似錦怨喘、人聲如沸津畸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肉拓。三九已至,卻和暖如春梳庆,著一層夾襖步出監(jiān)牢的瞬間暖途,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工膏执, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驻售,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓更米,卻偏偏與公主長得像欺栗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子征峦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1迟几、準(zhǔn)備 在分析探索Dubbo架構(gòu)原理之前,我們需要準(zhǔn)備一下環(huán)境栏笆,用于后面我們來分析dubbo的架構(gòu)类腮。 1.1 Z...
    墨淵丶閱讀 2,603評論 1 20
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)竖伯,斷路器存哲,智...
    卡卡羅2017閱讀 134,669評論 18 139
  • Dubbo是什么 Dubbo是Alibaba開源的分布式服務(wù)框架因宇,它最大的特點(diǎn)是按照分層的方式來架構(gòu)七婴,使用這種方式...
    Coselding閱讀 17,218評論 3 196
  • 你好哇祟偷,彩虹。 冬天的時(shí)候你會(huì)被人發(fā)現(xiàn)嗎打厘,你也許有地方去吧修肠,不是藏著 還有可能,你存在可人看不見
    左左翩西閱讀 172評論 0 0
  • 華佗,字元化,沛國譙人吗伤。又名甫方。在徐州游歷學(xué)習(xí),通曉幾種經(jīng)書硫眨。懂得修心養(yǎng)性的方法,年將五十還有壯年的容貌,當(dāng)時(shí)人...
    揮劍斬浮云hjzfycom閱讀 636評論 1 6