源碼解析
發送消息
以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中。