RocketMQ4.5源码分析之通信协议消息编解码机制

概述

通信协议的消息格式如下:

【4 byte totalLen】【4 byte headerLen】【headerLen byte header】【bodyLen byte body】

totalLen = 4 + headerLen + bodyLen,即等于二三四部分长度总和。

SerializeType.ROCKETMQ 类型消息头的编码格式如下:

【2 byte code】【1 byte language】【2 byte version】【4 byte opaque】【4 byte flag】【4 byte remarkLen】

【remarkLen byte remarkContent】【4 byte extFieldsLen】【extFieldsLen byte extFields】

消息头的extFieds数据编码格式如下:

【2 byte keyLen】【keyLen key】【4 byte valueLen】【valueLen byte value】

请求头的字段说明:

Header字段Request说明Response说明
code请求操作码RequestCode,应答方根据不同的请求码进行不同的业务处理应答响应码。0表示成功,非0则表示各种错误
language请求方实现的语言应答方实现的语言
version请求方程序的版本应答方程序的版本
opaque相当于reqeustId,在同一个连接上的不同请求标识码,与响应消息中的相对应应答不做修改直接返回
flag区分是普通RPC还是onewayRPC的标志区分是普通RPC还是onewayRPC的标志
remark传输自定义文本信息传输自定义文本信息
extFields请求自定义扩展信息响应自定义扩展信息

常用请求操作码RequestCode的说明:

codevalue
SEND_MESSAGE10
PULL_MESSAGE11
PUT_KV_CONFIG100
GET_KV_CONFIG101

通信协议消息解码

NettyDecoder#decode()方法

public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf frame = null;
        try {
// 将ByteBuf跳过4个字节
// NettyDecoder继承自LengthFieldBasedFrameDecoder
// public NettyDecoder() { super(FRAME_MAX_LENGTH, 0, 4, 0, 4); }
// 构造函数最后一个参数是initialBytesToSkip,即初始跳过的字节数
            frame = (ByteBuf) super.decode(ctx, in);
            if (null == frame) {
                return null;
            }

            ByteBuffer byteBuffer = frame.nioBuffer();
//netty在获取这条消息的时候是通过LengthFieldBasedFrameDecoder进行拆包的。
//该拆包的原理就是通过 消息的length长度进行拆分的。
//所以此时的byteBuffer是header length + header data +body data这部分。
            return RemotingCommand.decode(byteBuffer);
        } catch (Exception e) {
            log.error("decode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
            RemotingUtil.closeChannel(ctx.channel());
        } finally {
            if (null != frame) {
                frame.release();
            }
        }

        return null;
    }

 RemotingCommand#decode()方法

public static RemotingCommand decode(final ByteBuffer byteBuffer) {
        int length = byteBuffer.limit();
//获取前4个字节,组装int类型,高8位为serializeType,低24位为headerLen
        int oriHeaderLen = byteBuffer.getInt();
//oriHeaderLen & 0xFFFFFF 获取低24位的内容,即消息头的长度
        int headerLength = getHeaderLength(oriHeaderLen);

        byte[] headerData = new byte[headerLength];
//从ByteBuffer中读取headerLen个字节的数据,这个数据就是消息头的数据
        byteBuffer.get(headerData);
        RemotingCommand cmd = headerDecode(headerData, getProtocolType(oriHeaderLen));
//body的长度
        int bodyLength = length - 4 - headerLength;
        byte[] bodyData = null;
        if (bodyLength > 0) {
            bodyData = new byte[bodyLength];
//从ByteBuffer中读取bodyLen个字节的数据,这个数据就是消息体的数据
            byteBuffer.get(bodyData);
        }
        cmd.body = bodyData;

        return cmd;
    }

通信协议消息头解码

private static RemotingCommand headerDecode(byte[] headerData, SerializeType type) {
        switch (type) {
            case JSON:
                RemotingCommand resultJson = RemotingSerializable.decode(headerData, RemotingCommand.class);
                resultJson.setSerializeTypeCurrentRPC(type);
                return resultJson;
            case ROCKETMQ:
                RemotingCommand resultRMQ = RocketMQSerializable.rocketMQProtocolDecode(headerData);
                resultRMQ.setSerializeTypeCurrentRPC(type);
                return resultRMQ;
            default:
                break;
        }

        return null;
    }

SerializeType.ROCKETMQ 类型消息头解码

public static RemotingCommand rocketMQProtocolDecode(final byte[] headerArray) {
        RemotingCommand cmd = new RemotingCommand();
        ByteBuffer headerBuffer = ByteBuffer.wrap(headerArray);
        // int code(~32767)
        cmd.setCode(headerBuffer.getShort());
        // LanguageCode language
        cmd.setLanguage(LanguageCode.valueOf(headerBuffer.get()));
        // int version(~32767)
        cmd.setVersion(headerBuffer.getShort());
        // int opaque
        cmd.setOpaque(headerBuffer.getInt());
        // int flag
        cmd.setFlag(headerBuffer.getInt());
        // String remark
        int remarkLength = headerBuffer.getInt();
        if (remarkLength > 0) {
            byte[] remarkContent = new byte[remarkLength];
            headerBuffer.get(remarkContent);
            cmd.setRemark(new String(remarkContent, CHARSET_UTF8));
        }

        // HashMap<String, String> extFields
        int extFieldsLength = headerBuffer.getInt();
        if (extFieldsLength > 0) {
            byte[] extFieldsBytes = new byte[extFieldsLength];
            headerBuffer.get(extFieldsBytes);
            cmd.setExtFields(mapDeserialize(extFieldsBytes));
        }
        return cmd;
    }

SerializeType.JSON 类型消息头解码

直接把 json 数据反序列化成RemotingCommand对象

    public static <T> T decode(final byte[] data, Class<T> classOfT) {
        final String json = new String(data, CHARSET_UTF8);
        return fromJson(json, classOfT);
    }
    public static <T> T fromJson(String json, Class<T> classOfT) {
        return JSON.parseObject(json, classOfT);
    }

通信协议消息编码

NettyEncoder#encode()方法

public void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out)
        throws Exception {
        try {
            ByteBuffer header = remotingCommand.encodeHeader();
            out.writeBytes(header);
            byte[] body = remotingCommand.getBody();
            if (body != null) {
                out.writeBytes(body);
            }
        } catch (Exception e) {
            log.error("encode exception, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
            if (remotingCommand != null) {
                log.error(remotingCommand.toString());
            }
            RemotingUtil.closeChannel(ctx.channel());
        }
    }

通信协议消息头编码

RemotingCommand#encodeHeader()方法

public ByteBuffer encodeHeader(final int bodyLength) {
        // 1> header length size
        int length = 4;

        // 2> header data length
        byte[] headerData;
        headerData = this.headerEncode();

        length += headerData.length;

        // 3> body data length
        length += bodyLength;

        ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength);

        // length
//先放入消息的总大小
        result.putInt(length);

        // header length
//再放入serializeType和消息头的长度
//它是一个组装int类型,高8位为serializeType,低24位为headerLen
        result.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));

        // header data
//接着放入消息头数据
        result.put(headerData);

        result.flip();

        return result;
    }

RemotingCommand#headerEncode()方法

private byte[] headerEncode() {
//生成请求头的extFields数据
        this.makeCustomHeaderToNet();
        if (SerializeType.ROCKETMQ == serializeTypeCurrentRPC) {
            return RocketMQSerializable.rocketMQProtocolEncode(this);
        } else {
            return RemotingSerializable.encode(this);
        }
    }

 RocketMQSerializable#rocketMQProtocolEncode()方法

public static byte[] rocketMQProtocolEncode(RemotingCommand cmd) {
        // String remark
        byte[] remarkBytes = null;
        int remarkLen = 0;
        if (cmd.getRemark() != null && cmd.getRemark().length() > 0) {
            remarkBytes = cmd.getRemark().getBytes(CHARSET_UTF8);
            remarkLen = remarkBytes.length;
        }

        // HashMap<String, String> extFields
        byte[] extFieldsBytes = null;
        int extLen = 0;
        if (cmd.getExtFields() != null && !cmd.getExtFields().isEmpty()) {
            extFieldsBytes = mapSerialize(cmd.getExtFields());
            extLen = extFieldsBytes.length;
        }

        int totalLen = calTotalLen(remarkLen, extLen);

        ByteBuffer headerBuffer = ByteBuffer.allocate(totalLen);
        // int code(~32767)
        headerBuffer.putShort((short) cmd.getCode());
        // LanguageCode language
        headerBuffer.put(cmd.getLanguage().getCode());
        // int version(~32767)
        headerBuffer.putShort((short) cmd.getVersion());
        // int opaque
        headerBuffer.putInt(cmd.getOpaque());
        // int flag
        headerBuffer.putInt(cmd.getFlag());
        // String remark
        if (remarkBytes != null) {
            headerBuffer.putInt(remarkBytes.length);
            headerBuffer.put(remarkBytes);
        } else {
            headerBuffer.putInt(0);
        }
        // HashMap<String, String> extFields;
        if (extFieldsBytes != null) {
            headerBuffer.putInt(extFieldsBytes.length);
            headerBuffer.put(extFieldsBytes);
        } else {
            headerBuffer.putInt(0);
        }

        return headerBuffer.array();
    }

消息头的extFieds数据的生成和编码过程

DefaultMQProducerImpl#sendKernelImpl()方法

     SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
     requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
     requestHeader.setTopic(msg.getTopic());
     requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey());
       
 requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums());
     requestHeader.setQueueId(mq.getQueueId());
     requestHeader.setSysFlag(sysFlag);
     requestHeader.setBornTimestamp(System.currentTimeMillis());
     requestHeader.setFlag(msg.getFlag());
     requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
     requestHeader.setReconsumeTimes(0);
     requestHeader.setUnitMode(this.isUnitMode());
     requestHeader.setBatch(msg instanceof MessageBatch);              
.........

MQClientAPIImpl#sendMessage()方法

 RemotingCommand request = null;
        if (sendSmartMsg || msg instanceof MessageBatch) {
            SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
            request = RemotingCommand.createRequestCommand(msg instanceof MessageBatch ? RequestCode.SEND_BATCH_MESSAGE : RequestCode.SEND_MESSAGE_V2, requestHeaderV2);
        } else {
            request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);

RemotingCommand#createRequestCommand()方法

设置RemotingCommand的customHeader对象。

public static RemotingCommand createRequestCommand(int code, CommandCustomHeader customHeader) {
        RemotingCommand cmd = new RemotingCommand();
        cmd.setCode(code);
        cmd.customHeader = customHeader;
        setCmdVersion(cmd);
        return cmd;
    }

RemotingCommand#makeCustomHeaderToNet()方法

将RemotingCommand的customHeader对象的属性名和值,转为map集合extFields的key/value

private HashMap<String, String> extFields;

//将RemotingCommand的customHeader对象的属性名和值,转为map集合extFields的key/value
public void makeCustomHeaderToNet() {
        if (this.customHeader != null) {
            Field[] fields = getClazzFields(customHeader.getClass());
            if (null == this.extFields) {
                this.extFields = new HashMap<String, String>();
            }

            for (Field field : fields) {
                if (!Modifier.isStatic(field.getModifiers())) {
                    String name = field.getName();
                    if (!name.startsWith("this")) {
                        Object value = null;
                        try {
                            field.setAccessible(true);
                            value = field.get(this.customHeader);
                        } catch (Exception e) {
                            log.error("Failed to access field [{}]", name, e);
                        }

                        if (value != null) {
                            this.extFields.put(name, value.toString());
                        }
                    }
                }
            }
        }
    }

 RocketMQSerializable#mapSerialize()方法

用RemotingCommand的map集合extFields,生成请求头的extFields编码数据。

请求头的extFields数据的编码格式如下:

【2 byte keyLen】【keyLen key】【4 byte valueLen】【valueLen byte value】

public static byte[] mapSerialize(HashMap<String, String> map) {
        // keySize+key+valSize+val
        if (null == map || map.isEmpty())
            return null;

        int totalLength = 0;
        int kvLength;
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            if (entry.getKey() != null && entry.getValue() != null) {
                kvLength =
                    // keySize + Key
                    2 + entry.getKey().getBytes(CHARSET_UTF8).length
                        // valSize + val
                        + 4 + entry.getValue().getBytes(CHARSET_UTF8).length;
                totalLength += kvLength;
            }
        }

        ByteBuffer content = ByteBuffer.allocate(totalLength);
        byte[] key;
        byte[] val;
        it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            if (entry.getKey() != null && entry.getValue() != null) {
                key = entry.getKey().getBytes(CHARSET_UTF8);
                val = entry.getValue().getBytes(CHARSET_UTF8);

                content.putShort((short) key.length);
                content.put(key);

                content.putInt(val.length);
                content.put(val);
            }
        }

        return content.array();
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值