dubbo解析-从dubbo协议分析如何设计RPC协议

本文基于dubbo 2.7.5版本代码

本文介绍一下dubbo协议的组成,通过对dubbo协议的学习,为我们以后设计自己的RPC协议提供指导。在下一篇文章中从代码上介绍dubbo如何使用Netty。

一、dubbo协议

dubbo默认RPC协议是使用dubbo协议。dubbo协议分为报文头(也叫做Header)和报文体(也叫做Payload)。协议头占16个字节,协议体是可变长的,协议体是具体的请求/响应数据。下面是dubbo协议的组成,图片来源于官网:

http://dubbo.apache.org/zh-cn/blog/dubbo-protocol.html

在这里插入图片描述
各个字段的详细介绍:
报文头:

  • Magic - Magic High & Magic Low (2字节)
    标识协议版本号,Dubbo 协议:0xdabb,该字段是一个常量值。

  • Req/Res (1 bit)
    标识是请求或响应。请求: 1; 响应: 0。

  • 2 Way (1 bit)
    仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。一般应用发送的请求都是1。

  • Event (1 bit)
    标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。

  • Serialization ID (5 bit)
    标识序列化类型:比如 fastjson 的值为6。

  • Status (8 bits)
    仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。

    • 20 - OK
    • 30 - CLIENT_TIMEOUT
    • 31 - SERVER_TIMEOUT
    • 40 - BAD_REQUEST
    • 50 - BAD_RESPONSE
    • 60 - SERVICE_NOT_FOUND
    • 70 - SERVICE_ERROR
    • 80 - SERVER_ERROR
    • 90 - CLIENT_ERROR
    • 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
  • Request ID (64 bits)
    标识唯一请求。类型为long。该值是一个自增值。每申请一次增加1。

  • Data Length (32 bits)
    序列化后的内容长度(标识协议体的长度),按字节计数。int类型。

报文体:

  • Variable Part
    被特定的序列化类型(由序列化 ID 标识)序列化后的内容,每个部分都是一个 byte [] 或者 byte
    如果是请求包 ( Req/Res = 1),则每个部分依次为:
    Dubbo version,dubbo协议版本号,比如在2.7.5版本里面,dubbo version是2.0.2
    Service name,服务接口名
    Service version,服务的group值
    Method name,方法名
    Method parameter types,参数类型
    Method arguments,参数值
    Attachments,附录
    如果是响应包(Req/Res = 0),则每个部分依次为:
    返回值类型(byte),标识从服务器端返回的值类型:
    异常:RESPONSE_WITH_EXCEPTION=0
    正常响应值: RESPONSE_VALUE=1
    返回空值:RESPONSE_NULL_VALUE=2
    带附录的异常返回值:RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS=3
    带附录的正常响应值:RESPONSE_VALUE_WITH_ATTACHMENTS=4
    带附录的空值:RESPONSE_NULL_VALUE_WITH_ATTACHMENTS=5
    返回值:从服务端返回的响应bytes,如果返回值类型是2或者5,该字段是空
    Attachments:当返回值类型是3、4、5时,则在响应包里面添加附录信息,在2.7.5版本里面,附录值只有dubbo协议的版本号,也就是2.0.2。

从协议设计上可以看出,报文体最大不能超过2^31字节,相当于2G大小。

二、dubbo协议特性

  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO 异步传输
  • 序列化:Hessian 二进制序列化
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用

针对协议特性,我想到了下面几个问题,我们来看一下dubbo如何解决的。

  1. 为什么dubbo适合传入传出较小的参数数据包?当传输大包的时候会有什么影响?
    在官网上也对这个问题进行了分析。官网是从网络负载上分析的,包越大,网络负载越大,每秒传输的请求就越少。
    首先dubbo最大可以传输2G的数据,dubbo没有对报文压缩,如果传输大报文,造成网络传输数据量过大,可能会造成网络拥塞;默认消费端发送请求数据后等待服务端返回,大数据包造成网络传输时间长,消费端长时间等待;dubbo最大只能传输2G的数据,过大的包,dubbo无法处理;dubbo是为在微服务环境下快速响应请求的场景设计的,传输大数据包与此设计相违背。
  2. 长单连接如何实现?
    dubbo通过创建NettyClient对象建立与服务端的连接,如果多个不同的服务均由同一个服务端提供,那么使用相同的NettyClient对象。NettyClient负责启动Netty客户端,并建立与服务端的连接。NettyClient类的doConnect方法调用Bootstrap.connect建立与服务端的连接,连接建立完毕后便一直处于连接状态。
    默认情况下,每个服务端建立一个连接,可以通过参数“connections”修改连接数,当需要建立多个连接时,创建多个NettyClient对象。
  3. Netty使用异步数据传输,那么dubbo在哪些位置使用了异步?
    异步有很多好处:可以使用少量线程处理大量请求,避免客户端等待,减少资源占用。
    对于消费端,dubbo使用异步的地方是等待服务端返回值,可以通过参数“async”设置是否异步等待。
    对于服务端,Netty收到请求后,将请求交给handler,handler使用异步线程调用最终的服务。异步线程中完成下面几件事:请求报文反序列化,构建Response对象,调用过滤器,访问最终的服务,构造响应报文,响应报文序列化,将返回结果发送到消费端。

三、设计协议需要考虑的问题

我根据我现有的知识,总结了下面一些问题:

  1. 是否对报文体压缩?压缩报文可以减少报文传输量,有效降低网络负载,但是会增加CPU负担,而且报文头需要增加字段记录使用的压缩算法;
  2. 对于报文头的字段使用变长还是定长?比如dubbo协议里面的Date Length字段便可以考虑使用定长还是变长;
  3. 是否增加扩展字段?扩展字段为以后协议扩展带来方便;
  4. 是否在报文头增加控制字段,比如限流字段,权限字段?
  5. 字段与字段之间使用什么分隔符?dubbo是固定好各个字段的位置,我们设计的时候可以使用“;”作为分隔;
  6. 是否做判重处理?dubbo可以使用RPC Request ID作为请求的唯一标示,当出现同一个ID时,即认为是重复;
  7. 报文头字段值是尽量使用字符串表示还是使用二进制表示?比如dubbo协议的status字段既可以使用二进制表示,也可以使用字符串表示,使用字符串增加了报文的可读性,使用数字表示需要有解码器,但是二进制可以降低报文头的长度;
  8. 报文字段设计是力求极简还是力求功能完备?dubbo就属于力求极简的。极简的好处是网络层处理简单,但是上层会处理复杂一下;功能完备正好相反。

四、总结

从协议设计上看,dubbo协议还是比较简单的,我认为该协议含有的字段对一个RPC协议来说都是必不可少的,字段上不能比dubbo协议再少了。
当然因为字段少,字段含义简单,造成dubbo协议只能完成最基本的RPC服务调用。也因为这个原因,dubbo在报文组装上消耗的资源也少,可以快速的发送接收报文。
我们设计协议时,可以在此基础上做一些优化或者增加一些其他的特色,我这里举几个例子:

  1. 对报文体压缩,需要在报文头增加压缩类型字段,好处是减少报文包大小,但是处理报文时增加资源消耗;
  2. 压缩报文头,将少报文头部长度,比如将status融入到Serialization ID中去;
  3. Data Length、Request ID字段占了比较大的空间,但是很多时候,这两个字段的值都很小,也就是说有效数字比较少,对于这样的字段可以采用变长整数表示,数值非常小时,只需要使用一个字节来存储,数值稍微大一点可以使用 2 个字节,再大一点就是 3 个字节等等;
  4. 增加限流字段,对请求限流,将限流功能下移到网络传输层。

如果大家还有别的好的设计或者建议,我们可以在评论中讨论。

参考文章:
http://dubbo.apache.org/zh-cn/blog/dubbo-protocol.html
https://zhuanlan.zhihu.com/p/38012481

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值