RabbitMQ架构 - send message *

源码解析

发送消息

以basicPublish(…)方法为例。

这个方法用于发布一条消息。如果发布到一个不存在的交换器,会抛出通道级别的协议异常,会关闭通道。如果资源驱动的alarm有效,这个方法最终会阻塞。

先来看下它的重载方法。

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body) throws Exception;

void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) throws Exception;
  • exchange:交换器的名字。
  • routingKey:路由键。
  • mandatory:如果设置为true,当交换器无法根据自身的类型和路由键找到一个符合条件的队列时,那么RabbitMQ会调用Basic.Return命令将消息返回给生产者;如果设置为false,遇到这种情况,直接将消息丢弃。
  • immediate:如果设置为true,当交换器将消息路由到队列时,发现没有消费者,那么这条消息不会存入队列中。当与路由键匹配的所有队列都没有消费者时,该消息会通过Basic.Return返回给生产者。
  • body:消息体。
  • BasicProperties:相关的属性,如下。
    在这里插入图片描述

对于设置mandatory或者immediate参数值为true的情况,可以添加一个ReturnListener,用于对消息的监听。

e.g.

channel.addReturnListener(new ReturnListener() {
	@Override
	public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties, byte[] body) throws IOException {
		......
	}
});

实际上,是通过AutorecoveringChannel调用对应的basicPublish(...)方法。底层如下:

delegate.basicPublish(exchange, routingKey, mandatory, immediate, props, body);

delegate是RecoveryAwareChannelN类型

方法里面的处理如下:

if (nextPublishSeqNo > 0) {
    unconfirmedSet.add(getNextPublishSeqNo());
    nextPublishSeqNo++;
}
if (props == null) {
    props = MessageProperties.MINIMAL_BASIC;
}
AMQCommand command = new AMQCommand(
    new Basic.Publish.Builder()
        .exchange(exchange)
        .routingKey(routingKey)
        .mandatory(mandatory)
        .immediate(immediate)
        .build(), props, body);
try {
    transmit(command);
} catch (IOException e) {
    metricsCollector.basicPublishFailure(this, e);
    throw e;
}
metricsCollector.basicPublish(this);

nextPublishSeqNo:表示下一条发布的需要确认的消息的序列号。

unconfirmedSet:SortedSet类型,表示存储当前未确认消息的集合。

MessageProperties.MINIMAL_BASIC:用于构造MessageProperties实例。其中相关属性都是null。

AMQCommand:AMQP 0-9-1规范的Command的实现类。其封装了Method、AMQContentHeader、以及消息体。

在这里插入图片描述

_channelMutex:是一个对象锁。

ensureIsOpen()方法用来确保此时是开放状态,通过判断ShutdownSignalException属性是否为空来判断,如果它为空,表示此时是开放状态;否则表示此时是关闭状态。

在这里插入图片描述

hasContent():表示有消息头或者消息体。

TrafficListener#write(…):表示通知每一个向外的Command。以LogTrafficListener这个实现类为例,仅仅是日志记录。

接着看==AMQPCommand#transmit(…)==方法,如下:

public void transmit(AMQChannel channel) throws IOException {
	/* 获取_channelNumber属性,也就是通道编号 */
    int channelNumber = channel.getChannelNumber();
    /* 获取_connection属性,也就是关联的连接 */
    AMQConnection connection = channel.getConnection();
    /* CommandAssembler,负责从一系列的Frame中拼凑出一个命令 */
    synchronized (assembler) {
        Method m = this.assembler.getMethod();
        if (m.hasContent()) {
            byte[] body = this.assembler.getContentBody();
            Frame headerFrame = this.assembler.getContentHeader().toFrame(channelNumber, body.length);
			/* 获取_frameMax属性,也就是最大的Frame长度,0表示不受限制 */
            int frameMax = connection.getFrameMax();
            boolean cappedFrameMax = frameMax > 0;
            /* EMPTY_FRAME_SIZE :8 */
            int bodyPayloadMax = cappedFrameMax ? frameMax - EMPTY_FRAME_SIZE : body.length;
            if (cappedFrameMax && headerFrame.size() > frameMax) {
                String msg = String.format("Content headers exceeded max frame size: %d > %d", headerFrame.size(), frameMax);
                throw new IllegalArgumentException(msg);
            }
            connection.writeFrame(m.toFrame(channelNumber));
            connection.writeFrame(headerFrame);
            for (int offset = 0; offset < body.length; offset += bodyPayloadMax) {
                int remaining = body.length - offset;
                int fragmentLength = (remaining < bodyPayloadMax) ? remaining
                        : bodyPayloadMax;
                Frame frame = Frame.fromBodyFragment(channelNumber, body,
                        offset, fragmentLength);
                connection.writeFrame(frame);
            }
        } else {
            connection.writeFrame(m.toFrame(channelNumber));
        }
    }
    connection.flush();
}

CommandAssembler:负责从一系列的Frame中拼凑出一个命令。

Frame:表示AMQP wire-protocol的Frame,带有Frame的类型,通道编号和负载。

在这里插入图片描述

刷新缓冲区。实际上就是DataOutputStream调用flush()方法。
在这里插入图片描述

ErrorOnWriteListener接口:监听连接在socket上写数据是否遇到IO错误。这个可以用来触发连接恢复。
void handle(Connection connection, IOException exception) throws IOException;

发送消息到broker的核心方法
在这里插入图片描述

HeartbeatSender:管理心跳的发送。

public void signalActivity() {
    this.lastActivityTime = System.nanoTime();
}

将Frame写到数据连接中
在这里插入图片描述

在这里插入图片描述

accumulator:是ByteArrayOutputStream。

可以看出RabbitMQ生产者发送消息,底层是将相关信息写入到DataOutputStream中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章