在開發的時候,可能多個專案會修改同乙個服務,那麼不能直接暴露出來,否則會被其他人給呼叫到,導致資料不正常,那麼這種情況下可以使用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...