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

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

背景

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

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

没想到三歪做过改动,我就去问三歪,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注解条件下:

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

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

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


value为null

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


值为3

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

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


dubbo标签方式

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


value为0

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


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,如图:


retries为“0”

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

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

注解方式debug告一段落。


map不包含retries

后面是dubbo:reference标签形式:

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

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


dubbo自定义名称空间解析器

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


获取属性名的位置

获取retries值为0

小结

画个简单图:


大致流程

结论

  • 采用注解形式:不配置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,同事纷纷说我有点东西的更多相关文章

  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. JDBC 工具类封装

    每次使用jdbc 我们都要 加载驱动类 创建链接 创建Statement 接口对象执行sql 关闭资源 按照这样的套路可以封装一些重用代码方便在其他方法中调用 package com.xzlf.jdb ...

  2. 数据库 MySQL 练习

    一.sql语句基础 1.顯示德國 Germany 的人口 SELECT population FROM world  WHERE name = 'Germany' 2.查詢面積為 5,000,000 ...

  3. [Qt] 去掉QMessageBox标题栏上的图标

    msgBox.setWindowFlags(Qt::Drawer);

  4. 接近8000字的Spring/SpringBoot常用注解总结!安排!

    0.前言 大家好,我是 Guide 哥!这是我的 221 篇优质原创文章.如需转载,请在文首注明地址,蟹蟹! 本文已经收录进我的 75K Star 的 Java 开源项目 JavaGuide:http ...

  5. 【Linux题目】第六关

    [定时任务规则] 1. 如果在某用户的crontab文件中有以下记录,该行中的命令多久执行一次(RHCE考试题)?( ) 30 4 * * 3 mycmd A. 每小时. B. 每周. C. 每年三月 ...

  6. 解决Vue-cli3.0下scss文件编译过慢、卡顿问题

    在使用Vue-cli 3.0构建的项目中,可能存在项目编译过慢的问题,具体表现在编译时会在某一进度比如40%时停顿,等好一会儿才能够编译完成.这使得浏览器中的实时预览也会卡顿,不利于我们快速查看效果, ...

  7. mac OS Apache Tomcat 启动/停止服务

    进入Tomcat下的bin目录 启动Tomcat命令 ./startup.sh Tomcat 默认端口 8080 停止Tomcat服务命令 ./shutdown.sh 执行tomcat ./shutd ...

  8. Docker安装和基本操作

    一.Docker安装 CentOS7安装Docker CE $sudo yum install -y yum-utils device-mapper-persistent-data lvm2 $sud ...

  9. ROC-RK3328-CC开源主板运行LibreELEC系统

    LibreELEC是运行Kodi媒体中心的轻量级操作系统,基于Linux内核发行,系统为适配Kodi运行环境,做了许多优化和精简,运行速度快,操作简单.是一款很优秀的多功能播放器操作系统. ROC-R ...

  10. angularJS中$http.get( ).success( )报错原因及解决方案

    一.问题描述: 电脑安装的angular1.6.7版本,项目中使用了$http.get( ).success( ),控制台报错: $http.get(...).success is not a fun ...