一、背景

在使用dubbo服务中避免不了重试机制,平时新增、更新操作通常都会设置dubbo服务只调用一次,不然服务就需要做幂等,因为dubbo重试次数默认是2次,所以服务只调用一次需要设置retries =0,今天就来简单讲解下@Reference retries=0 这 一个小坑。

二、代码讲解

本文讲解的是Springboot集成Alibaba Dubbo版本

<dependency>
        <groupId>com.alibaba.boot</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>0.2.0</version>
</dependency>

注:这个集成包下用的是dubbo2.6.2版本,每个版本下可能会不同

首先我们来讲下@Reference retries 默认值,点开@Reference注解你会看到retries 的默认值0,这里别看到默认值为0就以为它的重试次数是0,那你就大错特错了,它其实默认重试次数是2次,为什么会这样呢,我们下面会讲到,继续往下看。。。

dubbo mock无效 dubbo retries=0 不生效_java

想设置重试次数为0次,则需要设置retries =0

dubbo mock无效 dubbo retries=0 不生效_java_02


接着需要设置使服务重试(具体代码就不贴出讲解,只讲解重点),结果会让你大吃一惊,你会发现它竟然重试了2次,我们明明设置了retries =0,为什么还会重试呢,我们带着疑问继续往下看。。。

dubbo mock无效 dubbo retries=0 不生效_spring boot_03

因为dubbo 容错缺省是Failover Cluster(失败自动切换),所以直接跟着断点来到了FailoverClusterInvoker这个类的doInvoke方法,上面有说到为什么retries 默认值0,dubbo的重试次数还是2次,就是因为在doInvoke这个方法里面处理了,doInvoke方法里调用了URL类中getMethodParameter方法来计算调用次数然后调用服务,这里看到参数里面retries 获取出来是null,所以它就返回了默认是2,加本身的一次调用就是3次,所以你看到len=3,证明了我们设置@Reference(retries = 0)没有效果,为什么会这样呢,我们重新打断点一步步查看问题在哪。。。

dubbo mock无效 dubbo retries=0 不生效_分布式_04

dubbo mock无效 dubbo retries=0 不生效_默认值_05


因为从参数里面获取retries 为null,所以就要找到为什么是null(因为断点很多,所以直接跳过其他,直接来到重点),跟着断点走到AnnotationUtils中的getAttributes方法中,有一个关键方法nullSafeEquals,该方法会传入当前属性值和默认值,如果相等,则会忽略掉该属性,然后将符合条件的属性放入actualAttributes这个map中,而我们的retries属性是0,和默认值一致,所以map中不会保存retries属性的值,只有timeout属性,因此出现了后面获取retries的值为null。

dubbo mock无效 dubbo retries=0 不生效_默认值_06


为什么设置@Reference(retries = 0)还是重试2次问题也就找到了!

但是带着学习的心态,我验证了dubbo几个版本,发现2.6.4版本以上修改了这个问题,我们稍微了看下2.6.4版本的调用。

<!--排除自带的dubbo 2.6.2版本,换为2.6.4版本-->
 <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.2.0</version>
      <exclusions>
            <exclusion>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo</artifactId>
            </exclusion>
      </exclusions>
  </dependency>
  <dependency>
  	<groupId>com.alibaba</groupId>
 	 <artifactId>dubbo</artifactId>
     <version>2.6.4</version>
  </dependency>

直接断点跳转到AnnotationUtils中 getAttributes的方法,getAttributes方面里面调用的getDefaultValue方法,获取retries 默认值为2(2.6.4以下版本返回的是0),所以往下执行往actualAttributes设值,那么FailoverClusterInvoker类中计算调用次数为1,那么我们设置@Reference(retries = 0)就有效了!

dubbo mock无效 dubbo retries=0 不生效_分布式_07


dubbo mock无效 dubbo retries=0 不生效_spring boot_08

dubbo mock无效 dubbo retries=0 不生效_java_09

三、结论

  1. dubbo 2.6.4版本以下使用@Reference(retries = 0)设置无效,retries 设置小于0的数值才有效
  2. dubbo 2.6.4以上版本修复了这个问题