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中。

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