一、RocketMq broker服务器与客户端的网络通信是基于netty4.x实现的,重点分析  RocketMq设计的通信协议及对应的编解码 开发。

        名字解释
                        编码:将java对象转换成二进制数据,用于放到网络中进行传输
                        解码:将从网络中读取到的二进制数据转换成相应的java对象
 
二、Remoting设计的通信协议格式如下(重点理解,能根据通信协议格式来对网络中读取的二进制数据进行编解码):

        协议格式 <length> <header length> <header data> <body data>
                                1                      2                        3                      4
 
        1、4个字节的int型数据来存储2、3、4的总长度
        2、4个字节的int型数据来存储报文头部的字节长度等于3的长度
        3、存储报文头部的数据
        4、存储报文体的数据
 
三、在通信过程中,服务器与客户端通过传递 RemotingCommand 对象来进行交互,下面将根据代码实现来分析通信协议的编解码开发
 
        3.1  分析RemotingCommand 类encode()方法,它将按照上述定义的协议格式进行对象的编码操作:
 
public ByteBuffer encode() {
        // 1> header length size
        int length = 4;   //表示用4个字节来存储头部长度
 
        // 2> header data length
        byte[] headerData = this.buildHeader();  //报文头部的数据
        length += headerData.length;                 //加上头部报文的字节长度
 
        // 3> body data length
        if (this.body != null) {
            length += body.length;                        //如果报文体body有数据则加上报文体的字节长度
        }
 
ByteBuffer result = ByteBuffer.allocate(4 + length);    //分配一个  (4+length)这么大的字节缓冲区,这个缓冲区就用来存储上述协议格式的整个报文的数据
 
        //下面代码开始往缓冲区存放数据
 
        // length                                         
        result.putInt(length);                              //缓冲区的最开始的4个字节用来存储总的长度length
 
        // header length
        result.putInt(headerData.length);        //缓冲区接下来4个字节用来存储报文头部的长度
 
        // header data
        result.put(headerData);                        //缓冲区接下来存储报文头部数据
 
        // body data;
        if (this.body != null) {
            result.put(this.body);                         //缓冲区最后用来存储报文体的数据
        }
 
        result.flip();                                            //将缓冲区翻转,用于将ByteBuffer放到网络通道中进行传输
 
        return result;
    }

       3.2  分析RemotingCommand 类decode()方法,它将按照上述定义的协议格式进行各个报文段的字节数据读取,然后转换成RemotingCommand对象:
        
public class NettyDecoder extends LengthFieldBasedFrameDecoder {
    private static final Logger log = LoggerFactory.getLogger(RemotingHelper.RemotingLogName);
    private static final int FRAME_MAX_LENGTH = //
            Integer.parseInt(System.getProperty("com.rocketmq.remoting.frameMaxLength", "8388608"));
 
 
    public NettyDecoder() {
        super(FRAME_MAX_LENGTH, 0, 4, 0, 4);        //0,4,0,4   每一个0,4,这个是表示存放长度的变量的字节所占的长度,为4个,第二个4表示就是解码之后的数据包跳过的字节数为4,表示就将数据包的头部给去掉了。
    }
 
//此处省略一万字
}
 
                    //在调用decode()方法解码之前,会调用上述NettyDecoder 类的decode()方法,在上述构造方法中,会先去掉报文的前4个字节,这4个字节是存储的后面报文的长度.
 
 public static RemotingCommand decode(final ByteBuffer byteBuffer) {
        int length = byteBuffer.limit();                             //获取字节缓冲区的整个长度,这个长度等于通信协议格式的2、3、4段的总长度
        int headerLength = byteBuffer.getInt();              //从缓冲区中读取4个字节的int类型的数据值 ,这个值就是报文头部的长度
 
        byte[] headerData = new byte[headerLength];  
        byteBuffer.get(headerData);                                //接下来从缓冲区中读取headerLength个字节的数据,这个数据就是报文头部的数据
 
        int bodyLength = length - 4 - headerLength;
        byte[] bodyData = null;                                       
        if (bodyLength > 0) {
            bodyData = new byte[bodyLength];
            byteBuffer.get(bodyData);                               //接下来读取length-4-headerLength  个字节的数据,这个数据就是报文体的数据
        }
  
//接下来将读取到的数据转换成   RemotingCommand 对象
        RemotingCommand cmd = RemotingSerializable.decode(headerData, RemotingCommand.class);      
        cmd.body = bodyData;
 
        return cmd;
    }
 
 
 
四 、总结
开发任何的socket 长连接的网络程序,涉及服务器与客户端的开发,首先要定义服务端与客户端的通信协议格式,第二根据定义的通信协议格式来进行

传输数据的编解码操作。  上述的通信协议格式为常用的通信协议格式。当长连接在设定的间隔时间范围内没有数据传输时,需要按照协议发送心跳包,

心跳包的协议格式也可以按照这种协议格式发送,也可以另外针对发送的心跳包来定义通信协议格式。

RocketMq通信协议格式及编解码 (源码分析)的更多相关文章

  1. RocketMQ中Broker的HA策略源码分析

    Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...

  2. RocketMQ中Broker的刷盘源码分析

    上一篇博客的最后简单提了下CommitLog的刷盘  [RocketMQ中Broker的消息存储源码分析] (这篇博客和上一篇有很大的联系) Broker的CommitLog刷盘会启动一个线程,不停地 ...

  3. ffplay源码分析5-图像格式转换

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10311376.html ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg ...

  4. RocketMQ中PullConsumer的消息拉取源码分析

    在PullConsumer中,有关消息的拉取RocketMQ提供了很多API,但总的来说分为两种,同步消息拉取和异步消息拉取 同步消息拉取以同步方式拉取消息都是通过DefaultMQPullConsu ...

  5. RocketMQ中Broker的启动源码分析(一)

    在RocketMQ中,使用BrokerStartup作为启动类,相较于NameServer的启动,Broker作为RocketMQ的核心可复杂得多 [RocketMQ中NameServer的启动源码分 ...

  6. RocketMQ中Broker的启动源码分析(二)

    接着上一篇博客  [RocketMQ中Broker的启动源码分析(一)] 在完成准备工作后,调用start方法: public static BrokerController start(Broker ...

  7. RocketMQ中Broker的消息存储源码分析

    Broker和前面分析过的NameServer类似,需要在Pipeline责任链上通过NettyServerHandler来处理消息 [RocketMQ中NameServer的启动源码分析] 实际上就 ...

  8. RocketMQ中PullConsumer的启动源码分析

    通过DefaultMQPullConsumer作为默认实现,这里的启动过程和Producer很相似,但相比复杂一些 [RocketMQ中Producer的启动源码分析] DefaultMQPullCo ...

  9. ffplay源码分析6-音频重采样

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10312713.html ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg ...

随机推荐

  1. 运行python程序不显示cmd的方法

    运行python程序的时候会在背景显示一个cmd,要想不显示其实很简单(虽然是我找了1个小时...才了解的基本知识) 方法1:pythonw xxx.py 方法2:将.py改成.pyw (这个其实就是 ...

  2. PAT L3-002. 堆栈

    树状数组,二分. 一堆数字,可以删除栈顶,压入数字,求中位数,可以线段树,也可以树状数组上二分. #include<map> #include<set> #include< ...

  3. LibreOJ #139 树链剖分 [树链剖分,线段树]

    题目传送门 树链剖分 题目描述 这是一道模板题. 给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值.下面依次进行 m 个操作,操作分为如下五种类型: 换根:将一个指定的 ...

  4. AndroidManifest.xml文件详解(permission-group)

    http://blog.csdn.net/think_soft/article/details/7578760 语法(SYNTAX): <permission-groupandroid:desc ...

  5. RabbitMQ (十一) 消息确认机制 - 消费者确认

    由于生产者和消费者不直接通信,生产者只负责把消息发送到队列,消费者只负责从队列获取消息(不管是push还是pull). 消息被"消费"后,是需要从队列中删除的.那怎么确认消息被&q ...

  6. [BZOJ2286][SDOI2011]消耗战(虚树DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4998  Solved: 1867[Submit][Statu ...

  7. AGC 018 A - Getting Difference

    题面在这里! 天呐,我已经做了一天水题了mmp 养生最重要,恩. 首先发现最终序列里的元素肯定是 <= max 的,因为无论何时序列里都不会有负数,所以减的话不会变大(反向大只有>2*ma ...

  8. [UOJ409]Highway Tolls

    题意:交互题,给定一个简单无向图和$A,B(1\leq A\lt B)$,你可以对每条边指定其边权为$A$或$B$后通过交互库询问$S\rightarrow T$的最短路($S,T$在程序运行之前已经 ...

  9. pymysql与mysql各功能

    pymysql # 增删改操作 import pymysql client=pymysql.connect( host='127.0.0.1', port=3306, user='root', pas ...

  10. LNMP一键包屏蔽PHP报错信息开启方法

    在 php-fpm.conf中的[www]下添加php_flag[display_errors] = On 重启php-fpm即可 /etc/init.d/php-fpm restart