我找到了Dubbo源码的BUG,同事纷纷说我有点东西

时间:2022-09-12 11:17:16

点赞再看,养成习惯,微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。

本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

背景

某天运营反馈,点了一次保存,但是后台出现了3条数据,我当时就想,不应该啊,这代码我几万年没动了,我当时就叫他先别操作了,保留一下现场,我去排查一下。

我看了下新增的代码,直接右键查看作者

我找到了Dubbo源码的BUG,同事纷纷说我有点东西

没想到三歪做过改动,我就去问三歪,XX模块的新增代码你是不是动过?

他沉默了很久没说话,然后抓起桌子上用剩下来的纸擦了擦鬓角留下的汗水,咽了一下口水说,是的我改过,我把之前dubbo的xml配置方式改成了注解的方式。

怎么了?现在出BUG了?

你呀你,下次这种改动跟我说一下,我估计是dubbo源码的bug吧,不要慌,让我去看看什么问题。

正文

其实dubbo配置的方式有很多种,大家用的最多的就是xml配置的方式,如果不需要重试次数,我们会加上重试次数为0,因为他默认是有多次的。

<dubbo:reference id="testService" interface="heiidea.trade.service.sdk.interfice.TestService" retries="0"/> 

或者使用注解的方式

@Reference(retries =0) 

其实我已经大概知道是什么原因了,但是为了证实自己的猜想,于是开启了接下来的debug之旅~~~

注:dubbo版本:2.6.2

首先是在采用@Reference注解条件下:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西

采用@Reference注解配置重试次数

首先是都找到了dubbo重试的代码位置(启动dubbo项目,到调用接口时,F5进入方法,会跳转到InvokerInvocationHandler中的invoke方法中,继续跟踪进入MockClusterInvoker中的invoke方法,然后进入AbstractClusterInvoker中的invoke方法中,这里主要是拿到配置的负载均衡策略,后面会到FailoverClusterInvoker的doInvoke方法中)。

重点来了,这里会获取配置的retries值,可以看到上面配置的是0,但是取出来居然是null,如图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
value为null

所以会返回defaultValue,加上本身调用的那一次,计算之后就会为3,如图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
值为3

所以可以发现,采用@Reference注解的形式配置retries为0时,dubbo重试次数为2次(3中包含本身调用的那次)。

后面是采用 dubbo:reference 标签的方式:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
dubbo标签方式

方式如上,在获取属性的时候,可以看到获得的值为0,和注解形式配置的一致,如图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
value为0

加上本身调用的那一次,计算之后就会为1,如图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
value为1

所以可以发现,采用 dubbo:reference 标签形式配置retries为0时,dubbo重试次数为0(1为本身调用的那次)。

原因分析

首先是@Reference注解形式:

dubbo会把每个接口先解析为ReferenceBean,加上ReferenceBean实现了FactoryBean接口,所以在注入的时候,会调用getObject方法,生成代理对象。

但是这不是关键,因为到这一步时,所有的属性都已经加载完成,所以需要找到dubbo解析注解中属性的代码位置。

dubbo会使用自定义驱动器ReferenceAnnotationBeanPostProcessor来注入属性,而具体执行注入的代码位置是在ReferenceAnnotationBeanPostProcessor类的postProcessPropertyValues方法中调用inject方法执行的。

重点来了,因为采用标签时,是采用@Autowired注解注入,所以是采用spring原生方式注入,而在采用@Reference注解时,注入时会走到dubbo自己的ReferenceAnnotationBeanPostProcessor中私有内部类ReferenceFieldElement的inject方法中,然后调用buildReferenceBean创建ReferenceBean。

离原因越来越近了,在该方法中可以看到beanBuilder中的retries值还是0,说明到这一步还没有被解析为null,如图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
retries为“0”

继续往下走,调用build方法中的configureBean时,在第一步preConfigureBean中方法,在该方法中会创建AnnotationPropertyValuesAdapter对象,在该对象构造方法中会调用adapt方法,然后走到AnnotationUtils中的getAttributes方法中,有一个关键方法nullSafeEquals,该方法会传入当前属性值和默认值。

如果相等,则会忽略掉该属性,然后将符合条件的属性放入actualAttributes这个map中,而我们的retries属性是0,和默认值一致,所以map中不会保存retries属性的值,只有timeout属性,因此出现了后面获取的值为null。

注解方式debug告一段落。

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
map不包含retries

后面是dubbo:reference标签形式:

上面说到了,标签形式走到inject时,会和注解形式有所不同,采用该标签时,dubbo会使用自定义的名称空间解析器去解析,很容易理解,spring也不知道它自定义标签里面那些玩意儿是什么意思,所以dubbo会继承spring的。

NamespaceHandlerSupport,采用自定义的DubboNamespaceHandler解析器来解析的标签,如下图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
dubbo自定义名称空间解析器

然后调用该类中的parse方法进行解析,而解析retries的地方就是获取class(此时的class就是上图绿色标明的ReferenceBean的class,其父类中有好多好多set方法,其中就包含setRetries方法)中所有的方法,过滤出set开头的方法,然后切割出属性名,放入属性池中,可以看到此处解析出的值为0,并不为null,如下图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
获取属性名的位置
我找到了Dubbo源码的BUG,同事纷纷说我有点东西
获取retries值为0

小结

画个简单图:

我找到了Dubbo源码的BUG,同事纷纷说我有点东西
大致流程

结论

  • 采用注解形式:不配置retries或者配置为0,都会重试两次,只有配置为 -1 或更小,才会不执行重试。

  • 采用标签形式:不配置retries会重试两次,配置为0或更小都不会重试。

所以建议大家不需要重试时可以设置为-1,比如增删改操作的接口,否则需要保证幂等性。需要重试则设置为1或更大,其实这应该算dubbo的一个dug吧?(我觉得是。。)

到这里就结束了,而上面说到的调用getObject方法就是后续服务发现以及和服务端建立长连接并返回代理对象了。

数据出现3条是因为我定义了接口超时的时间比较短,但是我们的新增涉及文件的操作,流程时间比较久,但是线程还是在的,所以dubbo重试了三次,三次也都是成功的了。

我后面把文件操作改成异步,然后主流程是同步的时间就缩短了很多。

补充:2.7.3版本已修复,就是在注解情况下,nullSafeEquals方法中的默认值和后面保持一致了,都是2,所以为0时也能保存到map中。

我是敖丙,一个在互联网苟且偷生的工具人。

你知道的越多,你不知道的越多人才们的 【三连】 就是丙丙创作的最大动力,我们下期见!

注:如果本篇博客有任何错误和建议,欢迎人才们留言,你快说句话啊


文章持续更新,可以微信搜索「 三太子敖丙 」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

我找到了Dubbo源码的BUG,同事纷纷说我有点东西

我找到了Dubbo源码的BUG,同事纷纷说我有点东西的更多相关文章

  1. Dubbo加权轮询负载均衡的源码和Bug,了解一下?

    本文是对于Dubbo负载均衡策略之一的加权随机算法的详细分析.从2.6.4版本聊起,该版本在某些情况下存在着比较严重的性能问题.由问题入手,层层深入,了解该算法在Dubbo中的演变过程,读懂它的前世今 ...

  2. dubbo源码分析6-telnet方式的管理实现

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  3. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  4. dubbo源码之四——服务发布二

    dubbo版本:2.5.4 2. 服务提供者暴露一个服务的详细过程 上图是服务提供者暴露服务的主过程: 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl ...

  5. 基于dubbo源码包通过Maven构建dubbo的详细步骤

    通过Maven构建dubbo 既然可以下载得到源码以及发布包,那么为什么要去构建dubbo呢?,我们先来看下dubbo的主要模块: 我们不仅要使用dubbo的核心框架,还要使用它的一些服务,比如管理控 ...

  6. dubbo源码分析一:整体分析

    本文作为dubbo源码分析的第一章,先从总体上来分析一下dubbo的代码架构.功能及优缺点,注意,本文只分析说明开源版本提供的代码及功能. 1.dubbo的代码架构:  spring适配层:常规的sp ...

  7. dubbo源码研究(一)

    1. dubbo源码研究(一) 1.1. dubbo启动加载过程 我们知道,现在流行注解方式,用spring管理服务,dubbo最常用的就是@Reference和@Service了,那么我首先找到这两 ...

  8. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  9. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

随机推荐

  1. Asp&period;Net alert 方法

    public static void ExcuteAlert(Page page, string strAlerts)        {            ClientScriptManager  ...

  2. 【BZOJ1007】【HNOI2008】水平可见直线

    依旧看黄学长代码,不过这回是看完后自己写的 原题: 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.例 ...

  3. HTML 元素

    HTML 文档是由 HTML 元素定义的. HTML 元素 HTML 元素指的是从开始标签(start tag)到结束标签(end tag)的所有代码. 开始标签 元素内容 结束标签 <p&gt ...

  4. nginx libpcre&period;so&period;1&colon; cannot open shared object file

    linux 64位安装nginx后启动出错报以下错误 1 2 3 [root@localhost nginx-1.3.0]# /usr/local/nginx/sbin/nginx error whi ...

  5. css3动画-animation

    animation驱使一组css style变化到另外一组css style,它可以定义keyframes的集合,指定style的开始和结束状态,它是transition的增强. 配置animatio ...

  6. 使用BeetleX的TcpBenchmark工具进行百万设备模拟测试

    其实TCP测试的工具有很多,那BeetleX工具所提供的特点又是什么呢?如果你需数十万的请求或模拟上百万的设备连接,那这个工具相信可以满足你的需要!工具是基于BeetleX的基础功能扩展,支持多IP绑 ...

  7. MySQL 查询重复数据,删除重复数据保留id最小的一条作为唯一数据

    开发背景: 最近在做一个批量数据导入到MySQL数据库的功能,从批量导入就可以知道,这样的数据在插入数据库之前是不会进行重复判断的,因此只有在全部数据导入进去以后在执行一条语句进行删除,保证数据唯一性 ...

  8. js 时间类函数

    js 时间类是  Date() var currtime = new Date();// 实例一个时间,是当前时间 接收一个时间戳为参数 var time2=new Date(currtime.get ...

  9. Django templates 模板的语法

    MVC 以及 MTV MVC: M : model -->> 存取数据(模型) V: view -->> 信息的展示(视图) C: controller -->> ...

  10. threejs 世界坐标转化为屏幕坐标

    网站: http://www.yanhuangxueyuan.com/Three.js_course/screen.html 方法.project 通过Vector3对象的方法project,方法的参 ...