dubbox注解的一个坑

时间:2022-11-01 21:37:08

  我和我同事Daniel排查的一个问题,原文是我同事Daniel写的,我做了些修改了补充。

  我们dubbox的provider端有很多service开发时没有考虑到幂等问题,于是只能暂时关掉dubbo的重试功能。

  dubbo自带的配置中有一个reties是可以配置重试次数的,官方文档上说默认两次,设置成0可以关闭重试。这种配置在使用xml 方式配置时是没有问题的,然而用注解就比较坑。

  dubbox注解的一个坑

  上图是同事写的使用注解的测试类截图,有没有发现0是灰色的?看看编译后的.class文件:

  dubbox注解的一个坑

  为什么呢,我们看这个注解的源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Reference { ... int retries() default 0; ... }

  这个注解有个默认值0,编译的时候被编译器发现,于是优化掉了。而调用的时候:

  dubbox注解的一个坑

  从注解中反射找reties,如果没找到就取默认值2,于是就造成了一个现象,只要不设置0就没问题,0就会等同于2。上图的完整方法:

    public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyinvokers = invokers;
checkInvokers(copyinvokers, invocation);
int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
for (int i = 0; i < len; i++) {
//重试时,进行重新选择,避免重试时invoker列表已发生变化.
//注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
if (i > 0) {
checkWheatherDestoried();
copyinvokers = list(invocation);
//重新检查一下
checkInvokers(copyinvokers, invocation);
}
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List)invoked);
try {
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + invocation.getMethodName()
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyinvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method "
+ invocation.getMethodName() + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyinvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
}

  这个方法就是远程调用中会执行的Invoke链中的一个,通过:

        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}

  上面这个判断可以猜到,文档中说的值为0关闭重试就是说的这句判断了。上一句中对反射得到的值进行了+1,这个+1就是加上了本身调用的一次,这里就是最大可能执行的总次数。文档中给出了一个方便人理解的值0来控制是否重试。由于一些原因只能用注解的方式,又无法使用0,这里的判断是<=0,那我们就在注解上设置-1或者更小都可以,于是结果就可以满足需要了。经过测试发现确实可以,于是问题解决的。注意,虽然问题解决了,但这个方式非常不好,只是限于一些原因只能暂时采用这种别扭的方式去做了,并不提倡。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      dubbox注解的一个坑

dubbox注解的一个坑的更多相关文章

  1. 使用lombok的&commat;Builder的注解的一个坑

    一开发说项目报错 java.lang.Long,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lan ...

  2. 关于fastjson的一个坑:输出json时,bean对象属性首字母默认被小写

    fastjson 是一个性能很好的 Java 语言实现的 JSON 解析器和生成器,来自阿里巴巴. 主要特点: 快速FAST: 比其它任何基于Java的解析器和生成器更快,包括jackson 强大:支 ...

  3. 用html5的视频元素所遇到的第一个坑

    html5 有一个video标签,这个是被大家所熟知的事情.按照w3c的规范,我认真的写出如下代码: <video preload="auto" controls=&quot ...

  4. 监控jvm的一个坑

    监控jvm的一个坑 1,遇到的问题 我按照以往文档,在catalina.sh里追加jvm的监控api,如下 紧接着我启动 tomcat. 未报任何错误. 发现 lsof –i:12000, 12000 ...

  5. JavaScript中sort方法的一个坑(leetcode 179&period; Largest Number)

    在做 Largest Number 这道题之前,我对 sort 方法的用法是非常自信的.我很清楚不传比较因子的排序会根据元素字典序(字符串的UNICODE码位点)来排,如果要根据大小排序,需要传入一个 ...

  6. PHP中逻辑运算符and&sol;or与&vert;&vert;&sol;&amp&semi;&amp&semi;的一个坑

    我原来以为PHP中的and和&&是一样的, 只是写法上为了可读性和美观, 事实上我错了. 这里面深藏了一个坑! 看以下代码: $bA = true; $bB = false; $b1  ...

  7. 困扰多日的C&num;调用Haskell问题竟然是Windows的一个坑

    最近一直被C#调用Haskell时的“尝试读取或写入受保护的内存”问题所困扰(详见C#调用haskell遭遇Attempted to read or write protected memory,C# ...

  8. 在VS2012中GridView的一个坑

    使用GridView的时候遇到了一个坑,一个增加一个选择按钮~貌似在某些情况下会出现一个是否允许选择的属性,貌似会默认为fals,然后就返回不了指定ID!坑,巨坑!但是今天居然找不到这个属性了,难道是 ...

  9. 【转载】linux命令行计算器bc的一个&OpenCurlyDoubleQuote;坑”

    [转载自]http://blog.chinaunix.net/uid-174325-id-3518953.html 结论:ibase,obase可以使用在不同的计算公式里,但是尽量把obase放iba ...

随机推荐

  1. &period;NET 环境中使用RabbitMQ

    在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方,比如发送短信 ...

  2. mysql 语法

    -create database database_name; show databases;show tables;                                 | -creat ...

  3. 在 ASP&period;NET 中创建数据访问和业务逻辑层(转)

    .NET Framework 4 当在 ASP.NET 中处理数据时,可从使用通用软件模式中受益.其中一种模式是将数据访问代码与控制数据访问或提供其他业务规则的业务逻辑代码分开.在此模式中,这两个层均 ...

  4. 读懂IL代码就这么简单&lpar;三&rpar;完结篇

    一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...

  5. 使用Js脚本 修改控制IE的注册表相关设置&lpar;activex等&rpar;

    使用Js脚本 修改控制IE的注册表相关设置(activex等) 分类: PHP2012-12-05 18:51 2035人阅读 评论(2) 收藏 举报 脚本写法: <SCRIPT LANGUAG ...

  6. maven插件:tomcat插件和jetty插件的区别

    在程序是多模块结构的时候,使用tomcat的maven插件和jetty的maven插件有细微差别: 1.tomcat7-maven-plugin   可以直接在parent的邮件直接运行:tomcat ...

  7. 你真的会使用SQL Server的备份还原功能吗?之一:恢复模型

    在SQL Server中,除了系统数据库外,你创建的每一个数据库都有三种可供选择的恢复模型: Simple(简单), full(完整), bulk-logged(批量日志). 下面这条语句可以显示出所 ...

  8. tomcat中浏览器重新选择下&period;就解决该问题了

  9. SSM前言——相关设计模式

    1.使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑.客户类真正的想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象. 2.静态代理是指,代理类在程序运行前就已经定义好,其与目 ...

  10. Redis备份

    Redis SAVE命令用来创建备份当前Redis数据库. 语法 Redis SAVE命令的基本语法如下所示: 127.0.0.1:6379> SAVE 例子 下面给出的例子创建备份当前的数据库 ...