写点什么

升级 dubbo,小心 default.version

用户头像
小楼
关注
发布于: 2021 年 04 月 02 日

上周遇到个关于升级 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



来自: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方法,所以这个修复就是无效的了。




欢迎关注我的公众号 “捉虫大师”



发布于: 2021 年 04 月 02 日阅读数: 13
用户头像

小楼

关注

还未添加个人签名 2018.09.19 加入

欢迎关注我的公众号“捉虫大师”

评论

发布
暂无评论
升级dubbo,小心default.version