升级dubbo,小心default.version

捉虫大师 等级 668 0 1

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

升级dubbo,小心default.version

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

这个issue有一个相关联的修复,说是2.7.7已经修复了这个问题,于是我测试了一下2.7.7,很遗憾,还是报错,看了下修复代码

升级dubbo,小心default.version

和2.6.6的兼容不一样,这里修复是在 URL 类的 valueOf 方法中添加兼容逻辑,修复者想的是所有注册中心上的URL字符串最终得经过这个方法才能成为URL对象,才能为dubbo所用。

想法是没错,但通过调试发现并不是每个URL对象都来自valueOf方法,2.7.7中订阅时对提供者的URL进行处理的是URLStrParser类的parseEncodedStr方法,所以这个修复就是无效的了。


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

升级dubbo,小心default.version

收藏
评论区

相关推荐

升级dubbo,小心default.version
上周遇到个关于升级dubbo 2.6 到2.7的兼容性问题,差点造成线上故障,这里记录下,也给大家提个醒。 问题回放有一个接口的提供方(dubbo 2.6.6)这么配置接口的版本号xml<dubbo:provider version"1.0.0"/消费方(也是dubbo 2.6.6)的reference这么配置xml<dubbo:
几个你不知道的dubbo注册中心细节
你会正确配置backup地址吗?在配置dubbo注册中心时,一般会这样写dubbo.registry.protocolzookeeperdubbo.registry.address127.0.0.1:2181也会简单地写成dubbo.registry.addresszookeeper://127.0.0.1:2181当z
聊聊dubbo协议
协议协议通俗易懂地解释就是通信双方需要遵循的约定。我们了解的常见的网络传输协议有tcp、udp、http等。再到我们常用的基础组件,一般来说client端与server端也有相应的协议,如redis、mysql、zookeeper等都是各自约定的私有协议,同样今天标题中的dubbo协议也是一种私有协议,他们都是应用层协议,基于tcp或udp设计。
聊聊dubbo协议2
在中介绍了attachments在consumer和provider间的传递情况,有个疑问没有给出答案。为什么2.7.x版本的dubbo不支持provider端向consumer端回传隐式参数呢?今天的续集将揭晓答案。 抓包确定是provider没发还是consumer丢掉 以下测试基于dubbo 2.7.6版本在provider端加入下面的代码
Dubbo 源码分析 - 服务调用过程
Dubbo 源码分析 服务调用过程注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 中阅读本系列文章。1\. 简介在前面的文章中,我们分析了 Dubbo SPI、服务导出与引入、以及集群容错方面的代码。经过前
Dubbo 源码分析 - 集群容错之 LoadBalance
Dubbo 源码分析 集群容错之 LoadBalance注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 中阅读本系列文章。1.简介LoadBalance 中文意思为负载均衡,它的职责是将网络
Dubbo 源码分析 - 集群容错之Directory
注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 中阅读本系列文章。1\. 简介前面文章分析了服务的导出与引用过程,从本篇文章开始,我将开始分析 Dubbo 集群容错方面的源码。这部分源码包含四个部分,分别是服务目录 Directory、服务路由 Router、集群 Cluster 和负载均衡 LoadBalance。这几个部分的源码逻辑比
dubbo应用级服务发现初体验
dubbo应用级服务发现介绍了解dubbo的朋友知道,dubbo的provider启动时向注册中心注册,consumer从注册中心消费。目前dubbo往注册中心上注册的数据是接口级,而应用级服务发现是往注册中心上注册实例(ip+port),两者的区别只是注册的粒度不同。至于为什么会出现应用级服务发现,有如下几点原因 与业界主流微服务模型对齐,比如 Sprin
dubbo的前世今生
背景在很久以前,网站应用是单体应用的架构,流量小,所有功能、代码都部署在一起,成本低。此时数据库访问框架ORM是关键。后来流量逐渐增大,单体应用被拆分为互不相干的多个应用,这就是垂直架构,此时加速前端页面开发的Web框架MVC是关键。再后来,垂直应用越来越大,应用间的交互不可避免,分布式服务框架RPC变成了关键。 dubboRPC,全称Remote Proc
给dubbo贡献源码,做梦都在修bug
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。 一在之前的文章《redis在微服务领域的贡献》中,从一次面试经历中了解了redis可以在微服务中玩的这么溜,同时也从源码角度分析了dubbo的redis注册中心。最后得出了dubbo的redis注册中心不能用于生产的结论,其中原因有如下两点: 使用了ke
dubbo 2.7应用级服务发现踩坑小记
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。 背景本文记录最近一位读者反馈的dubbo 2.7.x中应用级服务发现的问题,关于dubbo应用级服务发现的相关介绍可以参考之前的文章,这里不再赘述。读者反馈他们在基于dubbo 2.7应用级服务发现开发dubbo网关,根据文章《dubbo应用级服务发现初
作为一个码农终于把这些笔记看懂了,牛皮轰轰
Spring面试高频问题 SpringMVC面试高频问题 MyBatis面试高频问题 SpringBoot面试高频题 SpringCloud面试高频问题 Redis高级面试题 Dubbo高频常问面试问题 Java虚拟机(JVM) MySQL数据库高频面试问题 Java高频面试专题合集解析:当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,在这也是
使用dubbo-go搭建dubbo接口测试平台
背景http接口测试只需要一个curl命令,但dubbo协议没有这样的现成接口测试工具。通常公司内的dubbo控制台或其他平台会集成一个dubbo接口测试工具。调用一个dubbo接口,需要知道服务名service、方法名method和参数args。正常的调用,调用方需引入服务提供方定义的接口jar包。作为接口测试平台,没办法引入所有提供方定义的接口jar包,
Dubbo No provider问题排查思路
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。不想看字的同学可直接划到底部查看思维导图 问题分析使用过Dubbo的朋友很多都碰到过如下报错: No provider available for the service org.newboo.basic.api.MyDemoService from re
小白也能看懂的dubbo3应用级服务发现详解
搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。 本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。dubbo 是一款开源的 RPC 框架,主要有3个角色: 提供者(provider)、消费者(consumer) 、注册中心(registry)提供者启动时向