升級 dubbo 小心 default.version

捉蟲大師發表於2021-11-01

上週遇到個關於升級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&timestamp=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.取一次,也就是說versiondefault.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

image

來自:https://github.com/apache/dubbo/issues/5948

這個issue有一個相關聯的修復,說是2.7.7已經修復了這個問題,於是我測試了一下2.7.7,很遺憾,還是報錯,看了下修復程式碼

image

和2.6.6的相容不一樣,這裡修復是在 URL 類的 valueOf 方法中新增相容邏輯,修復者想的是所有註冊中心上的URL字串最終得經過這個方法才能成為URL物件,才能為dubbo所用。

想法是沒錯,但通過除錯發現並不是每個URL物件都來自valueOf方法,2.7.7中訂閱時對提供者的URL進行處理的是URLStrParser類的parseEncodedStr方法,所以這個修復就是無效的了。


關於作者:專注後端的中介軟體開發,公眾號"捉蟲大師"作者,關注我,給你最純粹的技術乾貨

image

相關文章