dubbo网络通信的编解码过程解析

写这篇文章的初衷是最近被别人问到dubbo的通信协议及底层序列化过程,发现自己比较欠缺这一块的知识。所以这个周末,结合了dubbo源码和一些网络教程,总结了一下。

编码和解码

首先做一下名词解释
编码:序列化,它将对象序列化为字节数组,用于网络传输,数据持久化或者其他用途。
解码:反序列化,把从网络,磁盘等读取的字节数还原成原始对象,以方便后续的业务逻辑操作。

dubbo在IP和TCP协议之上,又封装了自己的一层协议,为了解决TCP出现的粘包和拆包问题。
解决粘包和拆包问题有几个公认的方法:

  • 消息定长,例如固定为1000个字节
  • 在包尾增加回车或空格等特殊字符作为切割。典型的有FTP协议。
  • 将消息分为消息头和消息体。例如dubbo (dubbo的消息头是定长的16个字节)。

dubbo网络通信的编解码过程分为几个步骤:

  1. consumer请求编码
  2. provider请求解码
  3. provider的响应结果编码(如果有返回值的话)
  4. consumer的响应结果解码(如果有返回值的话)
consumer请求编码

首先说一下结论,然后对照代码解释一下

consumer请求编码调用栈
–>NettyCodecAdapter.InternalEncoder.encode
–>DubboCountCodec.encode
–>ExchangeCodec.encode
–>ExchangeCodec.encodeRequest
–>DubboCodec.encodeRequestData
第1-2个字节:一个魔数数字(就是一个固定的数字)
第3个字节:双向或单向的标记。双向是指请求有去有回,有返回值。单向是指请求有去无回,没有返回值。
第4个字节:在request请求中空着
第5-12个字节:请求id,long型的8个字节。异步变同步的全局唯一ID,用来做Consumer和Provider的来回通信标记。
第13-16个字节:消息体长度,也就是消息头+请求数据的长度。

以下是编码的入口函数
在这里插入图片描述
然后判断编码的类型是针对请求的还是请求响应的
在这里插入图片描述
然后是编码的主要方法
在这里插入图片描述
第214行是选择序列化组件
第215行HEADER_LENGTH==16,表示消息头是一个16个字节的数组。
第218行是在头2个字节中写入魔数,MAGIC是一个固定的数字
第221行到228行是在第3个字节处设置双向或者单向标记
231行,在第5个字节开始写入请求id,该id也用作请求返回的唯一标识
在这里插入图片描述
251行,从第13个字节开始写入消息体长度
在这里插入图片描述
最后就是写入消息

provider的请求解码

–>NettyCodecAdapter.InternalDecoder.messageReceived
–>DubboCountCodec.decode
–>ExchangeCodec.decode
–>ExchangeCodec.decodeBody
请求解码的字段意义就是上一步请求编码的字段意义。
以下是请求解码的关键方法:
在这里插入图片描述
可以看到这里是一个while循环,主要是为了解决TCP协议拆包的问题,如果没有达到指定的包长度,就循环的接收。如果出现粘包的问题,则根据消息体长度截断。
在这里插入图片描述
这里是ExchangeCodec.decode调用,主要是截取消息头。
在这里插入图片描述
这里是解析的关键方法。可以看到,首先校验一下头两个字节的魔数是否正确,如果不正确就直接返回。
然后判断头部长度是否是16,如果不是,也是返回异常结果。
在这里插入图片描述
然后再取出消息体长度,根据消息体长度来解包
在这里插入图片描述
解码内容过程先判断单向请求还是双向请求,然后分别处理
以双向请求为例,通过解码得到数据data的对象,该对象包括了rpc的方法名和参数以及dubbo版本号等一系列数据
在这里插入图片描述
至此,请求解码过程结束

provider的响应结果编码

–> NettyCodecAdapter.InternalEncoder.encode
–>DubboCountCodec.encode
–>ExchangeCodec.encode
–>ExchangeCodec.encodeResponse
–>DubboCodec.encodeResponseData

其中DubboCodec.encodeResponseData是先写入一个字节。这个字节可能是RESPONSE_VALUE或者RESPONSE_NULL_VALUE或者RESPONSE_WITH_EXCEPTION
响应结果编码的消息头还是一个16字节定长的数组
第1-2个字节:魔数数字(固定数字)
第3个字节:序列化组件类型,它用于和客户端约定序列化编码号
第4个字节:response的结果响应码,例如OK=20
第5-12个字节:请求id
第13-16个字节:消息体长度

因为请求以及结果响应的encode都是一套入口,所以首先还是判断encode类型是request还是response
在这里插入图片描述

然后按照字段意义设置byte数组,基本和请求的encode类似,但是需要注意的是第三个字节设置的是序列化组件的类型(比如hessian在dubbo中定义的类型是2),以及第4个字节是response的结果响应码,比如OK==20
在这里插入图片描述

在最后的encodeResponseData方法中会根据返回结果,塞入RESPONSE_VALUE或者RESPONSE_NULL_VALUE或者RESPONSE_WITH_EXCEPTION的响应码字节。
在这里插入图片描述
最后是放入attachments参数。

consumer的响应结果解码

consumer的响应结果解码和之前request的响应结果解码类似,就不赘述了。需要注意的是响应结果的第三个字节是序列化组件类型,第4个字节是response的结果响应码
–> NettyCodecAdapter.InternalDecoder.messageReceived
–>DubboCountCodec.decode
–>ExchangeCodec.decode
–>DubboCodec.decodeBody
–>DecodeableRPCResult.decode
其中DecodeableRPCResult.decode根据RESPONSE_VALUE或者RESPONSE_NULL_VALUE或者RESPONSE_WITH_EXCEPTION的响应码字节做相应的处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值