原创不易,转载请注明出处
文章目录
前言
打算用几篇文章解析一下dubbo序列化与反序列化部分,然后结束dubbo源码解析专栏,我们知道dubbo在调用的时候,会先收集调用信息封装到invocation中,到exchange层的时候,将invocation转成Request,在将这个Request交给传输层进行网络传输,其实在传输之前,会将Request序列化成二进制字节,最后交给TCP进行数据的发送,本文主要是介绍下,dubbo是怎样将进行序列化的,也就是怎样将Request转成二进制的。
1.入口
我们在介绍网络传输层的时候,介绍过基于netty4客户端创建流程,其中在添加netty handler的时候,就将encoder handler添加到netty的pipeline处理链中了,我们看下
NettyClient#doOpen
可以看到我用红框框起来的,创建了一个NettyCodecAdapter 对象,构造方法第一个参数就是codec,也就是编解码对象,这里这个codec的其实在NettyCodecAdapter 父类里面维护着,是在我们创建NettyClient 对象的时候赋的值。我们看下,这个是在AbstractEndpoint构造中
可以看到通过getChannelCodec 方法获取的codec。
可以看到,就是从url中获取codec的值,如果没有的话默认就是telnet,但是我们在前面就有代码将codec设置成了dubbo,然后通过dubbo spi获取Codec2的自适应对象,如果没有的话,就使用Codec实现,这里Codec2 dubbo实现是有的,也就是这个DubboCodec 。
2.请求是怎样序列化的
netty 将请求发送出去之前,会经过pipeline的处理,这个时候就会经过那几个handler,首先会经过nettyClientHandler,接着就是encoder,我们看下这个encoder是怎样处理的
调用了codec的encode方法,DubboCodec自己没有重写这个方法,而是调用父类ExchangeCodec 的encode 方法
ExchangeCodec#encode
因为我们是服务调用者发送调用请求,在Exchange层的时候将invocation转成了Request,所以是个Requset,走的encodeRequest 方法
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
Serialization serialization = getSerialization(channel);// 缺省hessian2
// header. 创建一个header的字节数组 16 byte
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag
// 使用 FLAG_REQUEST | 系列化id
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
// 设置是单向还是多项
if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
if (req.isEvent()) header[2] |= FLAG_EVENT;
///设置请求id
// set request id.
Bytes.long2bytes(req.getId(), header, 4);
// encode request data.
int savedWriteIndex = buffer.writerIndex();
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
// 使用serialization 对body进行序列化
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();
//body长度
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);
encodeRequest这段代码很长,也很复杂
这里稍微介绍下,首先是创建了一个16字节字节数组,
往0,1索引位置处添加了MAGIC,
往2索引位置处 放了FLAG_REQUEST ,系列化id,是否单向,是否事件。
3索引位置处没有用到(响应的时候能够用到)
从4索引位置处添加了一个long 也就是8个字节的请求id
然后 调用serialization.serialize 方法获取序列化的output流,这里默认使用的Hessian2
@Override
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
return new Hessian2ObjectOutput(out);
}
如果是事件就调用encodeEventData 方法,否则调用encodeRequestData 方法对内容信息进行编码,这两个方法是子类DubboCodec实现的,我们稍微看下encodeRequestData 方法
其实就是将请求信息写到这个output流里面,path,调用方法,参数类型,参数值这些东西。
再回到encodeRequest 方法中,接着就是执行这几行
out.flushBuffer();
bos.flush();
bos.close();
这几行其实就是将output流里面的内容刷到buffer 中。
获取内容的长度len,校验一下有没有超长
内容长度不能超过8m,否则抛出异常
再接下来往往12索引位置处写入4个字节的内容长度。
最后从buffer的起始位置处将header写进去。
可以对照这张图来看下
总结
本文主要是解析了服务调用者端是怎样将调用请求序列化成二进制的,下篇我们主要是介绍下服务提供者是怎样将请求二进制流反序列化成调用请求的。