由于网络的抖动的原因,经常在cat监控当中,看到dubbo的com.alibaba.dubbo.rpc.RpcException报错的时候;于是对于dubbo rpc流程,以及请求协议有了一定的兴趣;下面结合dubbo源码以及自己的学习体会,介绍一下dubbo rpc过程,以及dubbo默认的数据包结构;
dubbo 一次rpc过程
在看到dubbo rpc的过程,想起了head first 中介绍的命令模式(如下图模型),于是我将两者结合,帮助我理解dubbo rpc的流程(自己理解,不一定特别准确到位)
按照上面模式,将一次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,厨师)
![](https://img.haomeiwen.com/i5294752/1fc5741eec5fe02c.png)
图片来自网络于1
在dubbo源码中,有三个概念要非常的清楚:
- protocol : 它是invoker暴露 和引用的主功能入口,他负责Invoker的生命周期的管理
- invoker : 它是dubbo核心模型,其他模型都会向他靠拢,或是转换成它,它代表一个可执行体;可向他发一个远程的实现,也可以是一个集群的实现
- Invocation: 它持有调用过程的中变量,比如方法名,以及参数;dubbo请求实体序列化的过程中,主要是将Invacation实例进行序列化;
下面是官方介绍的,dubbo实现一次dubbo远程调用的过程;
![](https://img.haomeiwen.com/i5294752/86fe4784e4417ce2.jpg)
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:
网友评论