上週遇到個關於升級dubbo 2.6 到2.7的相容性問題,差點造成線上故障,這裡記錄下,也給大家提個醒。
問題回放
有一個介面的提供方(dubbo 2.6.6)這麼配置介面的版本號
<dubbo:provider version="1.0.0"/>
消費方(也是dubbo 2.6.6)的reference這麼配置
<dubbo:reference id="sampleService" version="1.0.0" check="false" interface="com.newboo.basic.api.SampleService"/>
然後升級消費方的dubbo版本
,經過一通操作將消費者升級到dubbo 2.7.3,預發測試時發現呼叫報No provider
,還好是在測試時發現,不然後果不堪設想
No provider available from registry 127.0.0.1:2181
根因分析
檢視註冊到註冊中心的URL是這樣
dubbo://10.0.0.6:20880/com.newboo.basic.api.SampleService?anyhost=true&application=ddog-provider-this-two&bind.ip=10.0.0.6&bind.port=20880&default.version=1.0.0&dubbo=2.0.2&generic=false&interface=com.newboo.basic.api.SampleService&methods=getByUid&owner=roshilikang&pid=82799&qos.accept.foreign.ip=true&qos.enable=true&side=provider×tamp=1616848403414
可以看到它沒有version
欄位,取而代之的是default.version
欄位
看一下dubbo中匹配version這個邏輯,位置在org.apache.dubbo.common.utils.UrlUtils
類的isMatch
方法,摘出重點部分
String consumerGroup = consumerUrl.getParameter(GROUP_KEY);
String consumerVersion = consumerUrl.getParameter(VERSION_KEY);
String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
String providerGroup = providerUrl.getParameter(GROUP_KEY);
String providerVersion = providerUrl.getParameter(VERSION_KEY);
String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
&& (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
&& (consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
邏輯很簡單,就是provider和consumer URL上的version
欄位得匹配上,提供者沒有version
欄位,只有default.version
欄位,很顯然呼叫時報錯。
但之前2.6.6是沒有問題的,為什麼?看了下2.6.6的實現,程式碼也是一樣,但點進providerUrl.getParameter(VERSION_KEY);
,發現它的實現不簡單
public String getParameter(String key) {
String value = parameters.get(key);
if (value == null || value.length() == 0) {
value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);
}
return value;
}
先取key對應的值,取不到時,再加個字首default.
取一次,也就是說version
和default.version
兩者只要有一個有值即可(version優先)。
反觀2.7.3的實現就非常耿直
public String getParameter(String key) {
return parameters.get(key);
}
所以,這就直接導致了2.7.3呼叫2.6.6的default.version介面報錯,類似的group也存在這個問題,甚至還有一些如timeout等引數都可能會失效。
這個問題還是比較明顯,應該有人遇到,搜尋了一下github,果然讓我找到了相關的issue
來自:https://github.com/apache/dubbo/issues/5948
這個issue有一個相關聯的修復,說是2.7.7已經修復了這個問題,於是我測試了一下2.7.7,很遺憾,還是報錯,看了下修復程式碼
和2.6.6的相容不一樣,這裡修復是在 URL
類的 valueOf
方法中新增相容邏輯,修復者想的是所有註冊中心上的URL字串最終得經過這個方法才能成為URL物件,才能為dubbo所用。
想法是沒錯,但通過除錯發現並不是每個URL物件都來自valueOf方法
,2.7.7中訂閱時對提供者的URL進行處理的是URLStrParser
類的parseEncodedStr
方法,所以這個修復就是無效的了。
關於作者:專注後端的中介軟體開發,公眾號"捉蟲大師"作者,關注我,給你最純粹的技術乾貨