dubbo异步调用的bug
来源:互联网 发布:数据质量提升方案 编辑:程序博客网 时间:2024/06/05 16:56
今天碰到一个dubbo异步调用引起的bug,特记录下来,以供其他人参考。
现象
现有3个服务,关系如下,serviceA异步调用serviceB,serviceB同步调用serviceC。其中serviceB暴露出的接口为异步方式。表现的现象为,serviceB每次调用serviceC时,第一次的返回结果为null,后面几次调用时均能正常返回结果。
问题排查
项目中对于所有的dubbo调用均有记录日志,每次调用主要包含2条日志,CS和CR日志。CS为consumer send,开始调用时日志。CR为接收到provider的应答日志。
通过观察日志发现。调用serviceC的日志有不合理的地方。在正常的同步调用中,如有多次调用,日志现实顺序为CS,CR,CS,CR。实际出现的日志顺序为CS,CS,CR,CR。初步怀疑第一次返回为null是由于异步调用所致。
通过dubbo admin查看接口的配置信息,发现参数均无异常。并且在serviceC端的provider上观察到每次调用均有返回数据。所以排除了serviceC的provider的问题。
再次编写testcase单独测试serviceC服务,发现testcase返回数据均正常。也证实了serviceC服务没有问题。
最后通过询问是否有异步配置时,发现serviceB暴露的接口是异步方式。修改serviceB为同步方式,重新测试,发现问题解决。每次调用均正常接收到结果。调整serviceB为异步,问题重现。
最终确认了问题发生场景,即serviceB为异步服务,在serviceB里面同步调用serviceC,最终表现为serivceC的调用是异步,导致问题产生。
代码分析
dubbo的provider和consumer均由Invoker演变而来。并且都是AbstractInvoker的实现类。
AbstractInvoker
Map<String, String> context = RpcContext.getContext().getAttachments(); if (context != null) { // A点 invocation.addAttachmentsIfAbsent(context); } // B点 if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){ invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString()); }
DubboInvoker
// C点boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);....if (isAsync) { ResponseFuture future = currentClient.request(inv, timeout) ; RpcContext.getContext().setFuture(new FutureAdapter<Object>(future)); // D点 return new RpcResult(); }
- 异步调用serviceB时,此时B点的判断为true,会在attachment中设置async的值为true,RpcContext中设置了async的值。
- 随后调用serviceB的实际方法。serviceB调用serviceC是,代码在A点,会将RpcContext的值设置到当前的invocation中
- 由于RpcContext是采用ThreadLocal保存的数据,并且在第一步时设置了async值,导致在2方法执行后,invocation中的async的值为true。
- 在进行serviceC的远程调用时,由于invocation中async值为true,导致C点判断为异步调用,在D点时new了一个RpcResult。所以在第一次调用serviceC时,返回结果为null。
- 第一次调用serviceC结束时,在consumer的filter chain中,有一个ConsumerContextFilter,在调用结束后会执行
RpcContext.getContext().clearAttachments()
方法,清除RpcContext中的信息,也就清除了async标识。 - 第二次调用serviceC时,由于RpcContext中没有了async标识,判断为同步调用,所以会正常返回结果。
解决方式
分析了问题产生的原因后,在不修改dubbo源码的情况,可以有一下几种处理方式。
1. 将serviceB改为同步调用,如果业务上确实需要异步调用,有以下2种处理方式
* serviceB的方法无需返回值,可采用oneway的方式
* 有返回值,并且需要异步,最简单的方式为在实现中使用线程池执行业务。
2. 增加一个Provider端的Filter,保证在filter链的结尾,在执行方法前,清除attachment中的async标志。也可达到同样的效果。
- dubbo异步调用的bug
- Dubbo异步方法调用里的问题
- Dubbo异步调用
- Dubbo-----异步调用
- dubbo异步调用
- Dubbo学习(十):异步调用
- Dubbo异步方法调用里有个坑
- dubbo异步调用传递性问题的解决方案
- 花擦节 dubbo异步调用变同步,解决异步调用返回值null的问题
- dubbo框架远程调用bug记录
- 淘宝SOA框架dubbo学习--异步调用
- dubbo异步同步调用混合使用问题
- dubbo 使用学习八(异步调用)
- dubbo 使用学习八(异步调用
- dubbo使用异步方式调用对象
- dubbo异步调用三种方式
- dubbo 常见的问题bug梳理
- 一个异步io中同步调用close造成数据传输不完整的bug。
- IntelliJ IDEA编译环境编写JSP文件报错且没有代码提示,还能正常运行
- Java进阶代码
- 生产者消费者模式实现
- Java学习——Character 类
- Haar Adaboost 视频车辆检测代码和样本
- dubbo异步调用的bug
- Python
- javaScript行内样式的获取及注意事项
- JavaEE的Struts2框架
- 一到n的乘积
- Python:把help的结果输出至文件
- Cookie简介
- 解决node-ffi在Windows XP上使用出现“Error: The specified procedure could not be found”的错误
- docker 部分常用命令及其功能笔记汇总