RocketMQ网络通信——协议

RocketMQ底层通信采用Netty,Netty为了解决粘包、半包问题提供了一些解码器:

  1. FixedLengthFrameDecoder:
    定长解码器,根据固定长度接收消息,容易浪费带宽且解决不了半包问题
  2. DelimiterBasedFrameDecoder
    特定分隔符解码器,如名,每次都要遍历到分隔符,效率较低
  3. LenghtFieldBasedFrameDecode
    消息头长度解码器,消息中会包含一些长度信息,根据这些长度信息解码,不错的选择

RocketMQ解码采用的是LenghtFieldBasedFrameDecode

public class NettyDecoder extends LengthFieldBasedFrameDecoder {
// 省略
}

编码

org.apache.rocketmq.remoting.netty.NettyEncoder#encode

@ChannelHandler.Sharable
public class NettyEncoder extends MessageToByteEncoder<RemotingCommand> {
    private static final Logger log = LoggerFactory.getLogger(LoggerName.ROCKETMQ_REMOTING_NAME);

    // 4个字节填充物 + 4字节总长度 + 1字节协议类型  + 3字节消息头长度 + 消息头 + 消息体
    @Override
    public void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out)
        throws Exception {
        try {
            remotingCommand.fastEncodeHeader(out);
            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());
            }
            RemotingHelper.closeChannel(ctx.channel());
        }
    }
}
    public void fastEncodeHeader(ByteBuf out) {
        int bodySize = this.body != null ? this.body.length : 0;
        // 拿到起始写位置
        int beginIndex = out.writerIndex();
        // skip 8 bytes
        // 为了凑齐2的整数幂(32位)
        out.writeLong(0);
        int headerSize;
        if (SerializeType.ROCKETMQ == serializeTypeCurrentRPC) {
            if (customHeader != null && !(customHeader instanceof FastCodesHeader)) {
                this.makeCustomHeaderToNet();
            }
            headerSize = RocketMQSerializable.rocketMQProtocolEncode(this, out);
        } else {
            this.makeCustomHeaderToNet();
            byte[] header = RemotingSerializable.encode(this);
            headerSize = header.length;
            out.writeBytes(header);
        }
        // 写入整个消息的长度,4为消息长度本身占用的长度
        out.setInt(beginIndex, 4 + headerSize + bodySize);
        // 跳过上一步设置的消息长度所占的4个字节,设置协议类型以及消息头长度(第一个字节为序列化类型,后面三个是消息头长度)
        out.setInt(beginIndex + 4, markProtocolType(headerSize, serializeTypeCurrentRPC));
    }

解码

org.apache.rocketmq.remoting.protocol.RemotingCommand#decode(io.netty.buffer.ByteBuf)

    public static RemotingCommand decode(final ByteBuf byteBuffer) throws RemotingCommandException {
        // 获取可读字节数
        int length = byteBuffer.readableBytes();
        // header长度4个字节32位
        int oriHeaderLen = byteBuffer.readInt();
        // 取oriHeaderLen低24位,前8位没有实际意义,只是为了凑2的整数幂(32)
        int headerLength = getHeaderLength(oriHeaderLen);
        if (headerLength > length - 4) {
            //todo-tzx 22480 2024/6/19 22:39 什么情况会走到这里?
            throw new RemotingCommandException("decode error, bad header length: " + headerLength);
        }
        // 解码header
        RemotingCommand cmd = headerDecode(byteBuffer, headerLength, getProtocolType(oriHeaderLen));
        // 解码body
        int bodyLength = length - 4 - headerLength;
        byte[] bodyData = null;
        if (bodyLength > 0) {
            bodyData = new byte[bodyLength];
            byteBuffer.readBytes(bodyData);
        }
        cmd.body = bodyData;

        return cmd;
    }

根据编解码代码可以得知,RocketMQ协议为:
4个字节填充物 + 4字节总长度 + 1字节协议类型 + 3字节消息头长度 + 消息头 + 消息体

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值