美文网首页
5.dubbo源码-编码&解码

5.dubbo源码-编码&解码

作者: 阿飞的博客 | 来源:发表于2017-11-06 17:21 被阅读262次

    Netty编码申明

    由于dubbo底层使用Netty作为网络传输框架,所以如果需要编码的话,可以通过继承netty的org.jboss.netty.handler.codec.oneone.OneToOneEncoder实现。dubbo编码实现在NettyCodecAdapter中(通过发布服务分析可知),自定义编码实现部分源码如下:

    @Sharable
    private class InternalEncoder extends OneToOneEncoder {
        @Override
        protected Object encode(ChannelHandlerContext ctx, Channel ch, Object msg) throws Exception{
            com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer =
                com.alibaba.dubbo.remoting.buffer.ChannelBuffers.dynamicBuffer(1024);
            NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
            try {
                codec.encode(channel, buffer, msg);
            } finally {
                NettyChannel.removeChannelIfDisconnected(ch);
            }
            return ChannelBuffers.wrappedBuffer(buffer.toByteBuffer());
        }
    }
    

    codec默认实现

    dubbo-rpc-default模块中/META-INF/dubbo/internal目录下文件com.alibaba.dubbo.remoting.Codec2中指定:
    dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec
    所以codec.encode(channel, buffer, msg);
    --> DubboCountCodec.encode(Channel channel, ChannelBuffer buffer, Object msg);
    --> ExchangeCodec.encode(Channel channel, ChannelBuffer buffer, Object msg),
    部分源码如下:

    public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
        if (msg instanceof Request) {
            encodeRequest(channel, buffer, (Request) msg);
        } else if (msg instanceof Response) {
            encodeResponse(channel, buffer, (Response) msg);
        } else {
            super.encode(channel, buffer, msg);
        }
    }
    

    以consumer调用provider为例,那么msg就是Request类型:所以接下来执行encodeRequest(channel, buffer, (Request) msg),这里执行的主要的业务逻辑:

    1. 获取具体的序列化实现,默认为hessian2:Serialization serialization = getSerialization(channel);
    2. 组装协议头
    3. 组装协议体encodeRequestData(channel, out, req.getData())并序列化写入buffer;
    4. 检查写入数据是否超载,默认为8M,可以通过payload设置,checkPayload(channel, len);
    5. 将协议头写入buffer;
    6. 在buffer上设置写的index;

    编码-组装协议头

    源码申明如下:byte[] header = new byte[HEADER_LENGTH];,所以协议头总计有16个byte;</br>
    第1步:</br>
    Bytes.short2bytes(MAGIC, header);
    所以协议头的前2个字节由MAGIC指定;</br>
    第2步:</br>
    header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
    第2个字节的计算方式,</br>
    第3步:</br>
    Bytes.long2bytes(req.getId(), header, 4);
    请求id赋值到消息头的4~11位置,占8个字节;</br>
    第4步:</br>
    Bytes.int2bytes(len, header, 12);
    消息体长度赋值到消息头的12~15位置,占4个字节;</br>

    编码-组装协议体

    实现源码在DubboCodec.encodeRequestData(Channel channel, ObjectOutput out, Object data):

    @Override
    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
        RpcInvocation inv = (RpcInvocation) data;
        out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
        out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
        out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
        out.writeUTF(inv.getMethodName());
        out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
        Object[] args = inv.getArguments();
        if (args != null)
        for (int i = 0; i < args.length; i++){
            out.writeObject(encodeInvocationArgument(channel, inv, i));
        }
        out.writeObject(inv.getAttachments());
    }
    

    由源码可知,消息体的内容如下:</br>
    1、dubbo版本号,例如:2.0.0</br>
    2、invoke的路径,例如:com.alibaba.dubbo.demo.TestService</br>
    3、invoke的provider端暴露的服务的版本号, 例如:0.0.0</br>
    4、调用的方法名称,例如:getTeacher</br>
    5、参数类型描述符,例如:Lcom/alibaba/dubbo/demo/bean/Student;</br>
    6、遍历请求参数值并编码;</br>
    7、dubbo请求的attachments:</br>

    相关文章

      网友评论

          本文标题:5.dubbo源码-编码&解码

          本文链接:https://www.haomeiwen.com/subject/rfnlmxtx.html