一、背景
在使用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次,为什么会这样呢,我们下面会讲到,继续往下看。。。
想设置重试次数为0次,则需要设置retries =0
接着需要设置使服务重试(具体代码就不贴出讲解,只讲解重点),结果会让你大吃一惊,你会发现它竟然重试了2次,我们明明设置了retries =0,为什么还会重试呢,我们带着疑问继续往下看。。。
因为dubbo 容错缺省是Failover Cluster(失败自动切换),所以直接跟着断点来到了FailoverClusterInvoker这个类的doInvoke方法,上面有说到为什么retries 默认值0,dubbo的重试次数还是2次,就是因为在doInvoke这个方法里面处理了,doInvoke方法里调用了URL类中getMethodParameter方法来计算调用次数然后调用服务,这里看到参数里面retries 获取出来是null,所以它就返回了默认是2,加本身的一次调用就是3次,所以你看到len=3,证明了我们设置@Reference(retries = 0)没有效果,为什么会这样呢,我们重新打断点一步步查看问题在哪。。。
因为从参数里面获取retries 为null,所以就要找到为什么是null(因为断点很多,所以直接跳过其他,直接来到重点),跟着断点走到AnnotationUtils中的getAttributes方法中,有一个关键方法nullSafeEquals,该方法会传入当前属性值和默认值,如果相等,则会忽略掉该属性,然后将符合条件的属性放入actualAttributes这个map中,而我们的retries属性是0,和默认值一致,所以map中不会保存retries属性的值,只有timeout属性,因此出现了后面获取retries的值为null。
为什么设置@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 2.6.4版本以下使用@Reference(retries = 0)设置无效,retries 设置小于0的数值才有效
- dubbo 2.6.4以上版本修复了这个问题