Dubbo-数据包结构解析

33 篇文章 0 订阅

Dubbo数据包

在这里插入图片描述
dubbo协议采用固定长度的消息头(16字节)和不定长度的消息体来进行数据传输,消息头定义了底层
框架(netty)在IO线程处理时需要的信息

协议详情

  • Magic - Magic High & Magic Low (16 bits)
    标识协议版本号,Dubbo 协议:0xdabb

  • Serialization ID (5 bit)
    标识序列化类型:比如 fastjson 的值为6

  • Event (1 bit)
    标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。

  • 2 Way (1 bit)
    仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器
    的返回值,则设置为1

  • Req/Res (1 bit)
    标识是请求或响应。请求: 1; 响应: 0

  • tatus (8 bits)
    仅在 Req/Res 为0(响应)时有用,用于标识响应的状态
    在这里插入图片描述

  • Request ID (64 bits) 标识唯一请求。类型为long。

  • Data Length (32 bits)
    序列化后的内容长度(可变部分),按字节计数。int类型

  • Variable Part
    被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte

在这里插入图片描述

对于(Variable Part)变长部分,当前版本的Dubbo 框架使用json序列化时,在每部分内容间
额外增加了换行符作为分隔,请在Variable Part的每个part后额外增加换行符

Dubbo version bytes (换行符)
Service name bytes (换行符)

*优点
协议设计上很紧凑,能用 1 个 bit 表示的,不会用一个 byte 来表示,比如 boolean 类型的标识。
请求、响应的 header 一致,通过序列化器对 content 组装特定的内容,代码实现起来简单

数据协议ExchangeCodec

public class ExchangeCodec extends TelnetCodec {

    // header length. 消息头的长度
    protected static final int HEADER_LENGTH = 16;
    // magic header. 标示为0-15位
    protected static final short MAGIC = (short) 0xdabb;
    protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0];
    protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1];
    // message flag. 消息头中的内容
    protected static final byte FLAG_REQUEST = (byte) 0x80;
    protected static final byte FLAG_TWOWAY = (byte) 0x40;
    protected static final byte FLAG_EVENT = (byte) 0x20;
    protected static final int SERIALIZATION_MASK = 0x1f;
    private static final Logger logger = LoggerFactory.getLogger(ExchangeCodec.class);

encode方法

  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 {
            //其他的交给上级处理,用于telnet模式
            super.encode(channel, buffer, msg);
        }
    }
encodeRequest
    protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
        //获取序列化方式
        Serialization serialization = getSerialization(channel);
        // header. 写入header信息
        byte[] header = new byte[HEADER_LENGTH];
        // set magic number. 魔数0-15位
        Bytes.short2bytes(MAGIC, header);
        //标记为请求
        // set request and serialization flag.
        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);
        //保存当前写入的位置,将其写入的位置往后面偏移,保留出写入内容大小的位置,先进行写入body内容
        // 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();
        //记录写入BODY的长度
        int len = bos.writtenBytes();
        checkPayload(channel, len);
        //将其写入到header中的位置中
        Bytes.int2bytes(len, header, 12);

        //发送到buffer中
        // write
        buffer.writerIndex(savedWriteIndex);
        buffer.writeBytes(header); // write header.
        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
    }
真正的 encodeRequestData 在子类 DubboCodec 中
    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
        RpcInvocation inv = (RpcInvocation) data;
        //写入版本
        out.writeUTF(version);
        // https://github.com/apache/dubbo/issues/6138
        //接口全名
        String serviceName = inv.getAttachment(INTERFACE_KEY);
        if (serviceName == null) {
            serviceName = inv.getAttachment(PATH_KEY);
        }
        out.writeUTF(serviceName);
        //接口版本号
        out.writeUTF(inv.getAttachment(VERSION_KEY));
        //方法名
        out.writeUTF(inv.getMethodName());
        //参数描述
        out.writeUTF(inv.getParameterTypesDesc());
        //参数
        Object[] args = inv.getArguments();
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                out.writeObject(encodeInvocationArgument(channel, inv, i));
            }
        }
        //写入所有的附加信息
        out.writeAttachments(inv.getObjectAttachments());
    }


encodeResponseData与上面的方法encodeRequestData 类似


    protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
        //
        Result result = (Result) data;
        // currently, the version value in Response records the version of Request
        // 是否支持返回attachment参数
        boolean attach = Version.isSupportResponseAttachment(version);
        Throwable th = result.getException();
        if (th == null) {
            //如果没有异常信息,则直接写入内容
            Object ret = result.getValue();
            if (ret == null) {
                out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS : RESPONSE_NULL_VALUE);
            } else {
                out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS : RESPONSE_VALUE);
                out.writeObject(ret);
            }
        } else {
            //否则的话则将异常信息序列化
            out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS : RESPONSE_WITH_EXCEPTION);
            out.writeThrowable(th);
        }

        //支持写入attachment,则写入
        if (attach) {
            // returns current version of Response to consumer side.
            result.getObjectAttachments().put(DUBBO_VERSION_KEY, Version.getProtocolVersion());
            out.writeAttachments(result.getObjectAttachments());
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值