RabbitMQ客户端源码分析(二)之Frame与FrameHandler

版本声明

  1. com.rabbitmq:amqp-client:4.3.0
  2. RabbitMQ版本声明: 3.6.15

Frame(帧)分析

AMQP帧(Frame)的格式

  1. tcp/ip是一个流协议,amqp没有采用在流中添加帧定界符来区分帧数据(原因官方认为简单,但是很慢),而是采用在header中写入帧大小来区分不同的帧(官方认为简单而且很快)。

  2. Frame(帧)就是传输协议的具体实现,即传输的具体数据包

    • 帧类型 通道编号 帧大小 内容 结束标记
  3. 从上面可以看到读取一个Frame,大概要实现的逻辑

    • 第一步:读取header,检查Frame的类型(1,2,3,8四种类型,AMQP.java类中定义)和Channel编号
    • 第二步:根据帧的类型(type)和payload的长度(size)来读取指定大小的payload进行处理
    • 第三步:读取最后一个字节(结束帧)。
    • 从上面的三步骤来看其实就是在做自定义协议解析的工作,不只是针对AMQP,其实自己定义的协议很多时候也是这么做的。
  4. AMQP协议定义了五种类型的帧:协议头帧method frame(方法帧)content header frame(内容头帧)content body(消息体帧)HEARTBEAT(心跳帧)

  5. 方法帧:一个Method Frame(方法帧)携带一个命令,方法帧payload有下面的格式

  6. Content Header的payload格式如下:

  7. Content Body Frame:内容是我们通常AMQP服务器在客户端与客户端之间传送和应用数据,内容头帧包含一条消息的大小和属性

Frame源码分析

  1. 详细的中文注释请访问Frame.java

  2. Frame的属性

    public class Frame {
         
         
        /** Frame type code */
        public final int type;
    
        /** Frame channel number, 0-65535 通道编号,一个TCPIP连接,多个通道,每个通道都有自己的编号*/
        public final int channel;
    
        /** Frame payload bytes (for inbound frames) 有效载荷,就是除了协议头之外的内容*/
        private final byte[] payload;
    
        /** Frame payload (for outbound frames) 字节流的累加器,用于*/
        private final ByteArrayOutputStream accumulator;
    
        /**
         * 自定义协议都是 【协议头】+【协议体】,NON_BODY_SIZE指的是没有协议体内容的情况下最小的传输
         * 长度。从这里可以看出【协议头】=type(1B)+channel(2B)+payloadSize(4B)+1(1B),总共占用8B(8个字节)
         */
        private static final int NON_BODY_SIZE = 1 /* type */ + 2 /* channel */ + 4 /* payload size */ + 1 /* end character */;
            
    }
    
    
  3. 读取Frame

    public static Frame readFrom(DataInputStream is) throws IOException {
         
         
        int type;
        int channel;
    
        try {
         
         
            //读取1B
            type = is.readUnsignedByte();
        } catch (SocketTimeoutException ste) {
         
         
            // System.err.println("Timed out waiting for a frame.");
            return null; // failed
        }
    
        if (type == 'A') {
         
         
            /*
             * Probably an AMQP.... header indicating a version
             * mismatch.
             */
            /*
             * Otherwise meaningless, so try to read the version,
             * and throw an exception, whether we read the version
             * okay or not.检查协议版本
             */
            protocolVersionMismatch(is);
        }
        //读取2B
        channel = is.readUnsignedShort();
        //读取4B
        int payloadSize = is.readInt();
        //构造存放内容的字节数组
        byte[] payload = new byte[payloadSize];
        /**
         *  readFully数据缓冲区的空间还有剩余时会阻塞等待读取,直到装满。
         *  此处不能使用is.read(payload),
         *  read(byte[] b)一直阻塞等待读取字节,直到字节流中的数据已经全部读完。
         *  而readFully(byte[] b)是当数据缓冲区的空间还有剩余时会阻塞等待读取,直到装满。
         */
    
        is.readFully(payload);
    
        int frameEndMarker = is.readUnsignedByte();
        //如果读取完body之后最后一个字节不是结束帧,就代表数据格式不正确,以此来判断Frame的传输的正确性
        if (frameEndMarker != AMQP.FRAME_END) {
         
         
            throw new MalformedFrameException("Bad frame end marker: " + frameEndMarker);
        }
    
        return new Frame(type, channel, payload);
    }
    

FrameHandler

  1. UML图
  2. rabbitmq-java-client(4.0.3)版本提供了两种处理模式,NIOBIO

SocketFrameHandler

  1. BIO模式,代码详细分析参考SocketFrameHandler

SocketChannelFrameHandler

  1. NIO模式,SocketChannelFrameHandler
  2. SocketChannelFrameHandler委托SocketChannelFrameHandlerState进行读写以及Socket选项设置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值