dubbo rpc 初探

2019-12-14  本文已影响0人  卫渐行

由于网络的抖动的原因,经常在cat监控当中,看到dubbo的com.alibaba.dubbo.rpc.RpcException报错的时候;于是对于dubbo rpc流程,以及请求协议有了一定的兴趣;下面结合dubbo源码以及自己的学习体会,介绍一下dubbo rpc过程,以及dubbo默认的数据包结构;

dubbo 一次rpc过程

在看到dubbo rpc的过程,想起了head first 中介绍的命令模式(如下图模型),于是我将两者结合,帮助我理解dubbo rpc的流程(自己理解,不一定特别准确到位)


image

按照上面模式,将一次dubbo的调用,划分几个角色:

  • 1: 顾客 本地的HelloService;
  • 2: 订单--> Invocation[sayHello("Hi")] dubbo会将通过rpc的请求封装成本地的代理类,然后通过proxy.invoke(invocation)的方法;能够标准化不同接口的RPC的处理规范
  • 3:女顾客,Invoke,是一个可执行的实体,所有的顾客的请求以及厨师做好的餐点,都要通过女顾客负责转接;客户端会将请求的实体,向女招待元靠拢,并且需要女招待传递到厨师那里;厨师做好的餐点也需要依赖女招待员传递给顾客
  • 4: 厨师:helloServiceImpl,负责方法的实现;

helloService(顾客) --sayHello("HI")----> dubboInvoker(将请求的实体打包成一个代理类,然后发起doInvoke(Invocation))
---Invoke(女招待)--->Requestor---send(request)-->socket-->socketChannelHandle--->socket--->Responseder()---invoke(Invocation 女招待)--->DubboExporter--->helloServiceImpl(getProxy and invoke,厨师)

helloService(顾客) --sayHello("HI")----> dubboInvoker(将请求的实体打包成一个代理类,然后发起doInvoke(Invocation))
---Invoke(女招待)--->Requestor---send(request)-->socket-->socketChannelHandle--->socket--->Responseder()---invoke(Invocation 女招待)--->DubboExporter--->helloServiceImpl(getProxy and invoke,厨师)

image

图片来自网络于1

在dubbo源码中,有三个概念要非常的清楚:

  • protocol : 它是invoker暴露 和引用的主功能入口,他负责Invoker的生命周期的管理
  • invoker : 它是dubbo核心模型,其他模型都会向他靠拢,或是转换成它,它代表一个可执行体;可向他发一个远程的实现,也可以是一个集群的实现
  • Invocation: 它持有调用过程的中变量,比如方法名,以及参数;dubbo请求实体序列化的过程中,主要是将Invacation实例进行序列化;

下面是官方介绍的,dubbo实现一次dubbo远程调用的过程;


image

dubbo rpc 协议

其中RPC协议指明了程序如何进行序列化和网络传输,也就是说一个RPC协议的实现等于一个非透明的RPC调用。下面主要介绍dubbo默认的自定义的dubbo协议;

  • request body payload:将请求的数据封装成Invocation(接口,参数类型,参数)以及attachement参数进行序列化,按照一定的规则,序列化成固定格式的二进制文件;
  • request header : 魔法数,标志位(请求,响应,事件ID),可以参考以下源码


    image
      Serialization serialization = getSerialization(channel);
    // header. 
    byte[] header = new byte[HEADER_LENGTH];
    // set magic number. 设置魔法数 dabb
    Bytes.short2bytes(MAGIC, header);
    // set request and serialization flag.
    header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
    //是否又返回值
    if (req.isTwoWay()) {
        header[2] |= FLAG_TWOWAY;
    }
    //是否为事件,heartbeat
    if (req.isEvent()) {
        header[2] |= FLAG_EVENT;
    }

    // set request id. 设置requestID,有返回值,根据ID返回,
    Bytes.long2bytes(req.getId(), header, 4);

    // encode request data. 对请求实体进行加密
    int savedWriteIndex = buffer.writerIndex();
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
    ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
    ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
    if (req.isEvent()) {
        encodeEventData(channel, out, req.getData());
    } else {
     //对请求实体,进行序列化
        encodeRequestData(channel, out, req.getData(), req.getVersion());
    }
    out.flushBuffer();
    if (out instanceof Cleanable) {
        ((Cleanable) out).cleanup();
    }
    bos.flush();
    bos.close();
    int len = bos.writtenBytes();
    //检查
    checkPayload(channel, len);
    Bytes.int2bytes(len, header, 12);

    // write 写入头部
    buffer.writerIndex(savedWriteIndex);
    buffer.writeBytes(header); // write header.
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);

上面是dubbo请求对象的编码过程,该过程首先会通过位运算将消息头写入到 header 数组中。然后对 Request 对象的 data 字段执行序列化操作,序列化后的数据最终会存储到 ChannelBuffer 中。序列化操作执行完后,可得到数据序列化后的长度 len,紧接着将 len 写入到 header 指定位置处。最后再将消息头字节数组 header 写入到 ChannelBuffer 中,整个编码过程就结束了

总结:

上述我们简单介绍了dubbo RPC的过程,并且介绍了三个重要的角色,protocol,invoke,invocation;这能帮助我们从宏观上理解一次RPC过程;其次我们介绍了dubbo自定义的协议,完成了dubbo中用什么数据格式传递数据的问题。

reference:

上一篇下一篇

猜你喜欢

热点阅读