社区编辑申请
注册/登录
我发现一个关于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,错误截图如下:

我觉得很奇怪,服务明明是启动的,也没有动态配置,为什么服务竟然会很奇怪的找不到呢?然后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客户端调用时序图如下(可以参考链接的泳道图):

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.

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

因此就淹没了序列化异常,导致真正的异常失真。这也是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如下:

责任编辑:武晓燕 来源: 中间件兴趣圈
相关推荐

2022-07-01 08:14:28

Dubbo异步代码

2022-06-28 09:34:24

可视化Python代码

2022-06-30 06:53:32

QQ泄露密码

2022-06-27 08:07:13

Go语言互斥锁

2022-06-26 09:55:00

接口自动化项目

2022-06-06 10:55:51

本地服务鸿蒙

2022-04-06 11:27:05

harmonyeTS 开发NAPI开发

2022-06-04 14:43:31

Windows 10出厂设置重装

2022-06-09 06:57:53

Windows 10Windows 11微软

2022-05-11 08:22:21

服务网关架构

2022-05-05 09:02:24

Go函数调用栈

2022-06-16 14:07:26

Java代码代码review

2022-06-06 09:56:38

编程语言Python

2022-05-24 08:21:16

数据安全API

2022-06-17 09:21:53

Pandas代码透视表

2022-05-24 15:46:51

Wi-FiSTA模式

2022-06-07 17:01:31

UI框架前端

2022-06-06 00:25:09

Golangpanic死锁

2022-06-13 14:58:19

系统案例

2022-06-13 09:45:51

Hook技术移动应用响应速度

同话题下的热门内容

应该知道的RPC内核细节(值得收藏)!!!SpringBoot+Nacos+Kafka实现微服务流编排如何评价Dooring低代码/零代码搭建平台?淘宝iOS扫一扫架构升级 - 设计模式的应用三张图带你彻底理解 RocketMQ 事务消息Grafana Loki 查询语言 LogQL 使用一个99%的人都说不清楚知识点—Spring 事务传播行为架构演变之SpringCloud由来

编辑推荐

终于有人把Elasticsearch原理讲透了!花了一个星期,我终于把RPC框架整明白了!拜托!面试不要再问我Spring Cloud底层原理陌陌基于K8s和Docker容器管理平台的架构实践收藏 | 第一次有人把“分布式事务”讲的这么简单明了
我收藏的内容
点赞
收藏

51CTO技术栈公众号