深度解析dubbo是怎样将请求序列化的

本文深入解析了 Dubbo 中请求的序列化过程。从 NettyClient 的创建流程开始,介绍了如何通过 NettyCodecAdapter 和 Codec 对象将 Request 转换为二进制字节。在编码过程中,详细讲解了如何构造头部信息、设置标志位、序列化请求数据等步骤。最后,检查内容长度并确保不超出限制,完成整个序列化流程。下篇将探讨服务提供者如何反序列化这些二进制数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原创不易,转载请注明出处


前言

打算用几篇文章解析一下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写进去。
可以对照这张图来看下
在这里插入图片描述

总结

本文主要是解析了服务调用者端是怎样将调用请求序列化成二进制的,下篇我们主要是介绍下服务提供者是怎样将请求二进制流反序列化成调用请求的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

$码出未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值