大数据架构师深入Dubbo远程调用:Dubbo核心调用流程+协议详解

本文详细介绍了Dubbo的RPC调用流程,包括核心调用步骤、Dubbo协议的结构与实现、编解码器工作原理,以及如何处理并发调用和网络粘包问题。重点讲解了协议头部和消息体的构成,以及Dubbo如何通过全局请求ID保证并发请求的正确响应。
摘要由CSDN通过智能技术生成

本章主要内容:

•Dubbo核心调用流程;

•Dubbo协议详解;

•Dubbo编解码器原理;

•Telnet调用原理;

•Dubbo线程模型。

本章首先介绍Dubbo的核心调用流程,接下来讲解Dubbo内部协议的设计和实现,通过对具体协议细节的理解,我们可以更好地掌握RPC通信的核心原理。在理解现有RPC协议的基础上,我们会对编解码器实现展开深入解析,同时对本地Telnet调用展开分析,最后对Dubbo线程模型进行深入探讨。

Dubbo调用介绍

在讲解Dubbo中的RPC调用细节之前,我们先回顾一次调用过程经历了哪些处理步骤。

如果我们动手写简单的RPC调用,则需要把服务调用信息传递到服务端,每次服务调用的一些公用的信息包括服务调用接口、方法名、方法参数类型和方法参数值等,在传递方法参数值时需要先序列化对象并经过网络传输到服务端,在服务端需要按照客户端序列化顺序再做一次反序列化来读取信息,然后拼装成请求对象进行服务反射调用,最终将调用结果再传给客户端。

在Dubbo中实现调用也是基于相同的原理,下面看一下Dubbo在一次完整的RPC调用流程中经过的步骤,如图6.1所示。

首先在客户端启动时会从注册中心拉取和订阅对应的服务列表,Cluster会把拉取的服务列表聚合成一个Invoker,每次RPC调用前会通过Directory#list获取providers地址(已经生成好的Invoker列表),获取这些服务列表给后续路由和负载均衡使用。对应图6.1,在①中主要是将多个服务提供者做聚合。在框架内部另外一个实现Directory接口是RegistryDirectory类,它和接口名是一对一的关系(每一个接口都有一个RegistryDirectory实例),主要负责拉取和订阅服务提供者、动态配置和路由项。

在Dubbo发起服务调用时,所有路由和负载均衡都是在客户端实现的。客户端服务调用首先会触发路由操作,然后将路由结果得到的服务列表作为负载均衡参数,经过负载均衡后会选出一台机器进行RPC调用,这3个步骤依次对应于②、③和④。客户端经过路由和负载均衡后,会将请求交给底层I/O线程池(比如Netty 处理,I/O线程池主要处理读写、序列化和反序列化等逻辑,因此这里一定不能阻塞操作,Dubbo也提供参数控制 decode.in.io 参数,在处理反序列化对象时会在业务线程池中处理。在⑤中包含两种类似的线程池,一种是I/O线程池Netty ,另一种是Dubbo业务线程池(承载业务方法调用)。

目前Dubbo将服务调用和Telnet调用做了端口复用,在编解码层面也做了适配。在Telnet调用时,会新建立一个TCP连接,传递接口、方法和JSON格式的参数进行服务调用,在编解码层面简单读取流中的字符串(因为不是Dubbo标准头报文),最终交给Telnet对应的Handler去解析方法调用。如果是非Telnet调用,则服务提供方会根据传递过来的接口、分组和版本信息查找Invoker对应的实例进行反射调用。在⑦中进行了端口复用,如果是Telnet调用,则先找到对应的Invoker进行方法调用。Telnet和正常RPC调用不一样的地方是序列化和反序列化使用的不是Hessian方式,而是直接使用fastjson进行处理。如果读者对目前的流程没有完全理解也没有关系,后面会逐渐深入讲解。

讲解完主要调用原理,我们接下来集中精力探讨更细节的一些知识点,比如Dubbo协议、编解码实现和线程模型等,本章篇幅主要放在⑤、⑥和⑦中,我们首先看一下目前Dubb。的协议细节。

Dubbo协议详解

本节我们讲解Dubbo协议设计,其协议设计参考了现有TCP/IP协议,在阅读图6-2时,我们发现一次RPC调用包括协议头和协议体两部分。16字节长的报文头部主要携带了魔法数(Oxdabb),以及当前请求报文是否是Request、Response 心跳和事件的信息,请求时也会携带当前报文体内序列化协议编号。除此之外,报文头部还携带了请求状态,以及请求唯一 标识和报文体长度。

理解协议本身的内容对后面的编码器和解码器的实现非常重要,我们先逐字节、逐比特位讲解协议内容(具体内容参考表6-l)。

在消息体中,客户端严格按照序列化顺序写入消息,服务端也会遵循相同的顺序读取消息,客户端发起请求的消息体依次保存下列内容:Dubb。版本号、服务接口名、服务接口版本、方法名、参数类型、方法参数值和请求额外参数(attachment)。

在协议报文头部的status中,完整状态响应码和作用如表6-2所示。

主要根据以下标记判断返回值,如表6-3所示。

在返回消息体中,会先把返回值状态标记写入输出流,根据标记状态判断RPC是否正常,比如一次正常RPC调用成功,则先往消息体中写一个标记1,紧接着再写方法返回值。

我们知道在网络通信中(基于TCP 需要解决网络粘包/解包的问题,一些常用解决办法比如用回车、换行、固定长度和特殊分隔符等进行处理,通过对前面协议的理解,我们很容易发现Dubbo其实就是用特殊符号exdabb魔法数来分割处理粘包问题的。

在实际使用场景中,客户端会使用多线程并发调用服务,Dubbo是如何做到正确响应调用线程的呢?关键点在于协议头全局请求id标识,我们先来看一下原理图,如图6-3所示。

当客户端多个线程并发请求时,框架内部会调用DefaultFuture对象的get方法进行等待。

在请求发起时,框架内部会创建Request对象,这个时候会被分配一个唯一 id, DefaultFuture可以从Request对象中获取id,并将关联关系存储到静态HashMap中,就是图6-3中的Futures集合。当客户端收到响应时,会根据Response对象中的id,从Futures集合中查找对应DefaultFuture对象,最终会唤醒对应的线程并通知结果。客户端也会启动一个定时扫描线程去探测超时没有返回的请求。

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值