采坑系列之--dubbo异步调用传递性导致嵌套调用返回null值的bug

栏目: 后端 · 发布时间: 5年前

内容简介:有三个应用serviceA,serviceB,serviceC,在确保消费没有错乱的前提下(都只有单个服务提供者),期望其调用关系为serviceA dubbo消费serviceB 配置为异步消费async="true",配置如下:然而,上面配置后,实际调用关系变为下图

有三个应用serviceA,serviceB,serviceC,在确保消费没有错乱的前提下(都只有单个服务提供者),期望其调用关系为

采坑系列之--dubbo异步调用传递性导致嵌套调用返回null值的bug

serviceA dubbo消费serviceB 配置为异步消费async="true",配置如下:

// serviceA 异步消费serviceB 配置如下
<dubbo:reference id="serviceB" interface="cn.ServiceB"
                     version="1.0.0" check="false">
        <dubbo:method name="reRunJob" async="true"/>
    </dubbo:reference>
复制代码
// serviceB 同步消费serviceC配置如下
<dubbo:reference id="serviceC" interface="cn.ServiceC"
                     version="1.0.0" check="false">
        <dubbo:method name="xxx"/>
    </dubbo:reference>
复制代码

然而,上面配置后,实际调用关系变为下图

采坑系列之--dubbo异步调用传递性导致嵌套调用返回null值的bug

如上所述,由于B->C 由于dubbo异步配置的传递性,导致变为了异步调用,结果返回了null,导致期望的同步调用结果异常, 但是B第二次调用C会正常返回

二、寻找问题根源--源码

1. 我们的排查思路

现象是由于dubbo异步调用,然后服务提供者内部又有dubbo嵌套调用,==所以我们需要找出dubbo的内部嵌套调用是否存在异步传递性==,那么既然是传递,就需要上下文环境,进而,我们想到了dubbo中的上下文环境==RpcContext==,我们推测是由此类传递了异步参数

2. 预备知识:RpcContext简介

RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。

比如:A调B,B再调C,则B机器上,在B调C之前,==RpcContext记录的是A调B的信息==,在B调C之后,RpcContext记录的是B调C的信息。

我们知道dubbo的方法调用,都是由invoker代理调用的,我们找到AbstractInvoker,查看底层的invoke方法,源码如下:

public Result invoke(Invocation inv) throws RpcException {
        ··· 省略无关代码
        
        // 这里的代码在此处对我们的serviceB-->serviceC时,会取出RpcContext,语句上面的RpcContext知识储备,我们知道,这里的context存的是serviceA->serviceB的上下文,也就是说是异步的,到这里谜团解开
        Map<String, String> context = RpcContext.getContext().getAttachments();
        if (context != null) {
        	invocation.addAttachmentsIfAbsent(context);
        }
        // 注意此处代码,如果提供者方法配置了异步参数
        if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
        // 会将异步参数值设置到当前调用对象中	invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
        }
        // 最后再将异步参数存入上下文
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        
        
        try {
            return doInvoke(invocation);
        } catch (InvocationTargetException e) {     ···
        } catch (RpcException e) {
            ···
        } catch (Throwable e) {
            ···
        }
    }
复制代码

3. 上面还有个小问题,serviceB第二次调用serviceC,会正常返回,这又是为什么呢?

这里涉及到一个Filter ConsumerContextFilter 源码如下:

@Activate(group = Constants.CONSUMER, order = -10000)
public class ConsumerContextFilter implements Filter {

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcContext.getContext()
                .setInvoker(invoker)
                .setInvocation(invocation)
                .setLocalAddress(NetUtils.getLocalHost(), 0)
                .setRemoteAddress(invoker.getUrl().getHost(), 
                                  invoker.getUrl().getPort());
        if (invocation instanceof RpcInvocation) {
            ((RpcInvocation)invocation).setInvoker(invoker);
        }
        try {
            return invoker.invoke(invocation);
        } finally {
           // ①注意这里 ,进行了上下文清理
          RpcContext.getContext().clearAttachments();
        }
    }

}
复制代码

==如上代码,serviceB第一次调用serviceC结束时,在consumer的filter chain中,有一个ConsumerContextFilter,在调用结束后会执行RpcContext.getContext().clearAttachments()方法,清除RpcContext中的信息,也就清除了async标识==,所以,第二次调用serviceC,就正常==同步==调用了,至此,我们的疑问得到解释

解决方法

分析了问题产生的原因后,在不修改dubbo源码的情况,可以有一下几种处理方式。

  1. 将serviceB改为同步调用,如果业务上确实需要异步调用,有以下2种处理方式

  2. serviceB的方法无需返回值,可采用oneway的方式(在消费者端配置dubbo:method中return="false")

  3. 有返回值,并且需要异步,最简单的方式为在实现中使用线程池执行业务。

  4. 增加一个Provider端的Filter,保证在filter链的结尾,在执行方法前,清除attachment中的async标志。也可达到同样的效果


以上所述就是小编给大家介绍的《采坑系列之--dubbo异步调用传递性导致嵌套调用返回null值的bug》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

复杂网络理论及其应用

复杂网络理论及其应用

汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元

国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码