Dubbo原始碼分析 多版本

2021-08-21 11:39:41 字數 4779 閱讀 3069

在開發的時候,可能多個專案會修改同乙個服務,那麼不能直接暴露出來,否則會被其他人給呼叫到,導致資料不正常,那麼這種情況下可以使用dubbo的多版本來解決這個問題,配置如下:

// 穩定環境下的provider和consumer

"com.foo.barservice" version="1.0.0" />

"barservice" inte***ce="com.foo.barservice" version="1.0.0" />

// 專案環境下的provider和consumer,我司用的是1.0.0加上專案字尾,這個只要能區分就ok

"com.foo.barservice" version="1.0.0***" />

"barservice" inte***ce="com.foo.barservice" version="1.0.0***" />

那麼穩定環境下的專案之間會呼叫穩定的服務,而專案環境中由於還在開發,介面可能不穩定,所以改了版本號,其他人呼叫不到(除非他們指定了你的非穩定版本,這種情況嘛….活該(〃』▽』〃)),那麼dubbo是如何區分這幾個服務的呢?接下來從consumer和provider原始碼開始分析

對於提供者來說,需要暴露兩個版本的服務,從zk上說的就是建立了兩個不一樣的節點。回顧一下當提供者接收到請求的時候,首先會先找到exporter,然後再找到invoker,那麼

- 如果是乙個機器上暴露了兩個版本的服務,這塊如何做區分呢?

這個問題要看下dubboprotocol類的export方法,因為這是講exporter儲存起來的地方

public

exporterexport(invokerinvoker) throws rpcexception

可以看到,如果要支援多版本,這個key肯定要不一樣,所以大概能猜出來,這個key的組成一定有version,那麼看下servicekey方法

protected

static string servicekey(url url)

//protocolutils

public

static string servicekey(url url)

public

static string servicekey(int port, string servicename, string serviceversion, string servicegroup)

if (serviceversion != null && serviceversion.length() > 0 && !"0.0.0".equals(serviceversion))

return buf.tostring();

}

引數傳了version,key的組成和version有關,另外還和group有關,這個屬性和分組有關,到這裡可以知道提供者這邊不同版本的服務有不同的exporter,進而也可以說明,消費者會把version這個屬性傳送過來,接下來看下消費者的處理

消費者這邊就比較複雜了,從zookeeperregistry的dosubscribe方法開始看起,因為這裡是處理zk相關節點的地方,而多版本在zk上是節點的不同,所以看下這裡是否有對節點做特殊處理

protected

void

dosubscribe(final url url, final notifylistener listener)

childlistener zklistener = listeners.get(listener);

if (zklistener == null)

});zklistener = listeners.get(listener);

}zkclient.create(path, false);

listchildren = zkclient.addchildlistener(path, zklistener);

if (children != null)

}notify(url, listener, urls);

//....

}

主要看providers的類目,在addchildlistener方法呼叫後,會返回兒子節點,即服務提供者節點,這時候,呼叫tourlswithempty方法

private listtourlswithempty(url consumer, string path, listproviders) 

return urls;

}

進來後,會再呼叫tourlswithoutempty進行處理,並返回乙個list,當list為空,會構建乙個empty協議的url,這個後面講到,如果不為空,則直接返回,那麼看下tourlswithoutempty方法

private listtourlswithoutempty(url consumer, listproviders) }}

}return urls;

}

可以看到,會遍歷提供者節點,和消費者進行比對,如果符合,那麼才會返回,到這裡就可以知道了urlutils.ismatch會有version的判斷

public

static

boolean

ismatch(url consumerurl, url providerurl)

if (! providerurl.getparameter(constants.enabled_key, true)

&& ! constants.any_value.equals(consumerurl.getparameter(constants.enabled_key)))

string consumergroup = consumerurl.getparameter(constants.group_key);

string consumerversion = consumerurl.getparameter(constants.version_key);

string consumerclassifier = consumerurl.getparameter(constants.classifier_key, constants.any_value);

string providergroup = providerurl.getparameter(constants.group_key);

string providerversion = providerurl.getparameter(constants.version_key);

string providerclassifier = providerurl.getparameter(constants.classifier_key, constants.any_value);

return (constants.any_value.equals(consumergroup) || stringutils.isequals(consumergroup, providergroup) || stringutils.iscontains(consumergroup, providergroup))

&& (constants.any_value.equals(consumerversion) || stringutils.isequals(consumerversion, providerversion))

&& (consumerclassifier == null || constants.any_value.equals(consumerclassifier) || stringutils.isequals(consumerclassifier, providerclassifier));

}

方法裡比較了很多引數,這裡我們只關心version,version不一樣的時候,會返回false,即tourlswithempty方法會返回乙個empty協議的url。

回到dosubscribe方法,獲取到urls之後,會呼叫notify方法,該方法一路呼叫到com.alibaba.dubbo.registry.integration.registrydirectory#refreshinvoker方法

private

void

refreshinvoker(listinvokerurls) else

}

這裡判斷url如果是empty協議的,那麼forbidden會設定為true,即禁止訪問,那麼會有什麼後果呢?

在com.alibaba.dubbo.registry.integration.registrydirectory#dolist方法中會首先判斷該屬性

public list> dolist(invocation invocation) 

//....

}

即呼叫的時候會報錯

那麼又有乙個問題

對服務暴露和提供熟悉的應該會知道,消費者對providers節點設定了***,當節點變化,然後呼叫一下notify方法,如果新增的節點是匹配版本的那麼refreshinvoker中forbidden不為true,服務正常呼叫,如果不匹配,那麼和上面一樣的結果。

總結一下:

1. 提供者不同服務會再zk上建立不同的節點

2. 提供者儲存exporter的時候會根據version的不同去構造不同的key放到map中

3. 消費者會獲取providers下的節點,並比對版本是否一樣,如果不一樣則返回乙個empty協議的url

4. 如果協議為empty,那麼會將forbidden設定為true,呼叫會報錯

5. 當有節點發生變化又會執行比對的過程

Dubbo原始碼分析

dubbo原始碼分析 其實已經有很多比較好的原始碼分析部落格,結合部落格和開發經驗再去分析原始碼,就能對dubbo的實現有個整體全面的理解,也能深入去深究其中的具體實現細節。dubbo裡主要用到的spi service provider inte ce netty nio 同步非阻塞多路復用框架,d...

Dubbo服務註冊原始碼分析

服務在本地發布完成,那麼接下去要進入服務的註冊階段 final registry registry getregistry origininvoker final url registeredproviderurl geturltoregistry providerurl,registryurl d...

Dubbo原始碼分析之SPI(二)

本篇文章是dubbo spi原始碼分析的第二篇,接著第一篇繼續分析dubbo spi的內容,我們主要介紹 getdefaultextension 獲取預設擴充套件點方法。由於此方法比較簡單,我們略過示例部分,直接分析原始碼。獲取預設擴充套件方法getdefaultextension 是乙個publi...