当前位置: 首页 > 科技观察

发现一个关于Dubbo服务调用理论知识的Bug

时间:2023-03-20 19:34:24 科技观察

结合我对Dubbo的理解,通常在dubbo调用中会出现Noprovideravailablefortheservicexxx。原因通常有以下几种:1、服务器没有启动。2、代码中client和server的组和版本不匹配。3.有dubbo标签路由过滤,标签不匹配。4.动态配置过滤,没有匹配的服务(如disable等)。但是这次遇到了上面没有的问题,于是做了一番研究,发现了dubbo在实现上的一些缺陷。后台在做JT808协议命令数据上行命令,通过808平台(netty长连接)采集命令,解析后通过dubbo调用服务,进行命令的业务逻辑处理。奇怪的是服务是存在的,但是报错Noprovideravailablefortheservicecom.xxx.ioc.api.service.JTService,报错截图如下:我觉得很奇怪,服务明明已经启动了,而且没有动态配置,为什么会奇怪的发现服务呢?然后查看debug下的代码,发现是dubbo编码阶段报错。io.netty.handler.codec.EncoderException:java.lang.RuntimeException:序列化类com.xxx.ioc.codec.util.KeyValuePair必须实现java.io.SerializableJava字段:私有com.xxx.ioc.codec.util。KeyValuePaircom.xxx.ioc.protocol.t808.T0900.message。原来是参数T009的内部类KeyValuePair没有实现序列化,但是如果没有实现序列化应该报错Serializedclassxxxmustimplementjava.io.Serializable错误,但是为什么收到的错误是Noprovideravailablefor服务xxx,带着这个问题,分析一下。分析过程的调用链接是根据自己之前分析的dubbo传输层记录。dubbo客户端调用时序图如下(可以参考链接的泳道图):dubbo客户端调用的基本流程如下:客户端通过netty管道的TailContext进行处理,业务线程切换到reactorIO线程,业务线程阻塞在DefaultFuture.get()等待响应。dubbo的编解码是在reactorIO线程中处理的。如果编码抛出异常,消息将不会发送到服务器。此时将异常(错误码BAD_REQUEST)封装为Response,然后业务线程被唤醒,阻塞在DefaultFuture.get()等待。这里有个问题,编码失败,那么如何返回响应信息呢?下篇文章会分析),执行com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#returnFromResponse,将异常信息封装为RemotingException并抛出代码如下:privateObjectreturnFromResponse()throwsRemotingException{Responseres=response;if(res==null){thrownewIllegalStateException("responsecannotbenull");}if(res.getStatus()==Response.OK){returnres.getResult();}if(res.getStatus()==Response.CLIENT_TIMEOUT||res.getStatus()==Response.SERVER_TIMEOUT){thrownewTimeoutException(res.getStatus()==Response.SERVER_TIMEOUT,channel,res.getErrorMessage());}thrownewRemotingException(channel,res.getErrorMessage());//序列化异常(错误码BAD_REQUEST)被封装为RemotingException向上抛出}关注调用链异常处理:异常在com.alibaba.dubbo中被捕获。rpc.protocol.dubbo.DubboInvoker#doInvoke,异常信息封装为RpcException(异常代码=NETWORK_EXCEPTION)向上抛出。然后捕获com.alibaba.dubbo.rpc.protocol.AbstractInvoker#invoke中的RpcException异常,由于异常code=NETWORK_EXCEPTION,不是业务异常代码,所以继续向上抛异常。最后在com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke中捕获异常RpcException。由于是failover故障转移策略,默认重试2次,所以再尝试调用其他节点。如果服务节点数量少如果重试次数+1(即3次),没有匹配到服务节点,那么在com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker#checkInvokers操作中,将报告一个错误Noprovideravailablefortheservicexxx。详细代码调用截图如下:因此,序列化异常被淹没,导致真正的异常被扭曲。这也是dubbo报错提示的一个小问题。如果要修复它,解决方案很简单:FailoverClusterInvoker添加以下方法:)){if(le!=null){抛出le;}checkInvokers(invokers,invocation);//请求父类}}并修改方法doInvoke如下: