我发现一个关于Dubbo服务调用的一个BUG

简介: 我发现一个关于Dubbo服务调用的一个BUG

我在使用Dubbo的过程中遇到了如下错误:No provider available for the service,本以为这个是服务提供者不存在,但经过我的排查,别的客户端是能够正确使用该服务的,原来背后蕴含Dubbo一个BUG。


理论知识


结合我对Dubbo的理解,通常dubbo调用出现 No provider available for the service xxx,其原因通常如下:


1.服务方未启动

2.代码内客户端和服务端的group、version不匹配

3.有dubbo tag路由过滤,标签不匹配

4.动态配置过滤,没有匹配的服务(比如disable等)


但这次遇到一个非以上问题,因此研究了一番,发现了dubbo在实现上有一些瑕疵。


背景


在做JT808协议指令数据上行指令,指令通过808采集平台(netty长连接),解析后,通过dubbo调用服务,做指令的业务逻辑处理,奇怪是服务存在,但是却报错No provider available for the service com.xxx.ioc.api.service.JTService,错误截图如下:

fb53d54b56b1bcf68ea0858127a311e2.png

我觉得很奇怪,服务明明是启动的,也没有动态配置,为什么服务竟然会很奇怪的找不到呢?然后debug下看了代码,发现是dubbo编码阶段报错io.netty.handler.codec.EncoderException: java.lang.RuntimeException: Serialized class com.xxx.ioc.codec.util.KeyValuePair must implement java.io.Serializable Java field: private com.xxx.ioc.codec.util.KeyValuePair com.xxx.ioc.protocol.t808.T0900.message原来是参数T009的内部类KeyValuePair未实现序列化导致,但是如果是未实现序列化,应该报错Serialized class xxx must implement java.io.Serializable的错误,但是为什么收到的错误却是No provider available for the service xxx,带着这个问题,分析一波。


分析过程


调用链路根据之前自己分析的dubbo transport层记录,dubbo客户端调用时序图如下(可以参考链接的泳道图):

d171e77017f9d83550af17fd359b7a8c.jpg

dubbo客户端的调用的基本流程说明如下:


  • 客户端经netty pipeline的TailContext处理,业务线程切换到reactor IO线程,业务线程在DefaultFuture.get()阻塞等待响应。
  • dubbo的编码/解码是在reactor IO线程处理,编码抛出异常,消息不会发送给服务方,此时异常(错误码BAD_REQUEST)被被封装为Response,继而唤醒业务线程在DefaultFuture.get()阻塞等待。


这里有个疑问,编码失败,那么是如何返回响应消息的呢?后面下篇文章分析),在执行com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#returnFromResponse,把异常信息封装为RemotingException进行抛出,代码如下:


private Object returnFromResponse() throws RemotingException {
    Response res = response;
    if (res == null) {
        throw new IllegalStateException("response cannot be null");
    }
    if (res.getStatus() == Response.OK) {
        return res.getResult();
    }
    if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
        throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
    }
    throw new RemotingException(channel, res.getErrorMessage());//序列化异常(错误码BAD_REQUEST)被封装为RemotingException向上抛出
}

重点关注一下调用链中的异常处理:


  • com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke内异常被catch,异常信息被封装为RpcException(异常code=NETWORK_EXCEPTION)向上抛。
  • 接着在com.alibaba.dubbo.rpc.protocol.AbstractInvoker#invoke内异常RpcException被catch,由于异常code=NETWORK_EXCEPTION,非业务异常代码,因此异常继续向上抛
  • 最后异常RpcException在com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke内被catch,由于是failover失效转移策略默认重试2次,因此接着尝试去调用调用其它节点,如果服务的节点数少于重试的次数+1(即3次),则没有匹配的服务节点,因此在com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker#checkInvokers操作内,会报错No provider available for the service xxx


详细的代码调用截图如下所示:

f134e06e6464f16cca43fa23572f8737.png

176f52cd9d404a0821802d6359c1852f.png

因此就淹没了序列化异常,导致真正的异常失真。这也是dubbo错误提示的一点小问题,如果要修复,解决方法也简单:FailoverClusterInvoker新增如下方法

private void checkInvokers(List<Invoker<T>> invokers, Invocation invocation, RpcException le) {
    if (invokers == null || invokers.isEmpty()) {
        if (le != null) {
            throw le;
        }
        checkInvokers(invokers, invocation);//请求父类
    }
}

同时修改方法doInvoke如下

c0905851d512c305ce933f6131b8f1ae.png

相关文章
|
5月前
|
Dubbo Java 应用服务中间件
从源码全面解析 dubbo 服务端服务调用的来龙去脉
从源码全面解析 dubbo 服务端服务调用的来龙去脉
|
5月前
|
缓存 负载均衡 Dubbo
从源码全面解析 dubbo 消费端服务调用的来龙去脉
从源码全面解析 dubbo 消费端服务调用的来龙去脉
|
5月前
|
Dubbo 应用服务中间件 Nacos
bug篇之基于docker安装nacos(2.1.1)使用dubbo连接不上的问题
bug篇之基于docker安装nacos(2.1.1)使用dubbo连接不上的问题
|
10月前
|
XML 编解码 JSON
不满足于RPC,详解Dubbo的服务调用链路(2)
不满足于RPC,详解Dubbo的服务调用链路
188 0
|
10月前
|
负载均衡 Dubbo 应用服务中间件
不满足于RPC,详解Dubbo的服务调用链路(1)
不满足于RPC,详解Dubbo的服务调用链路
258 0
|
开发框架 Dubbo 网络协议
SpringCloud + Gateway(网关) + Nacos(注册中心+配置中心)+ Dubbo(内部服务调用)
SpringCloud + Gateway(网关) + Nacos(注册中心+配置中心)+ Dubbo(内部服务调用)
1862 0
SpringCloud + Gateway(网关) + Nacos(注册中心+配置中心)+ Dubbo(内部服务调用)
|
负载均衡 Dubbo Java
Dubbo服务调用过程
前几篇文章讲述了 Dubbo 的注册和消费的流程、今天继续接着说下调用的流程(只谈及单注册中心、多注册中心只是前面多了一步、根据区域或者配置选择出其中一个注册中心而已)
163 0
|
Arthas 监控 Dubbo
案例分享 | dubbo 2.7.12 bug导致线上故障
最近某天的深夜,刚洗完澡就接到业务方打来电话,说他们的 dubbo 服务出故障了,要我协助排查一下。 电话里,询问了他们几点 是线上有损故障吗?——是 止损了吗?——止损了 有保留现场吗?——没有
217 0
案例分享 | dubbo 2.7.12 bug导致线上故障
|
存储 缓存 NoSQL
给dubbo贡献源码,做梦都在修bug
使用了keys命令,会阻塞单线程的redis,keys执行期间,其他命令都得排队 没有心跳检测这个功能,我测试了provider被kill -9杀死后,consumer是无法感知的。但从实现上来看是想通过存储的过期时间来判断服务是否可用,即需要对比url对应的value与当前的时间,如果过期应被剔除,但这部分貌似没有实现完整
133 0
给dubbo贡献源码,做梦都在修bug
|
负载均衡 Dubbo 算法
够强!一行代码就修复了我提的Dubbo的Bug。
够强!一行代码就修复了我提的Dubbo的Bug。
123 0