源碼解析
queueBind
以channel.queueBind(...)
爲例,展開分析。
將一個隊列綁定到一個交換器。
Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException;
Queue.BinkOk queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
void queueBindNoWait(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
很明顯前兩種是帶有響應返回值,後一種沒有響應返回值。
- queue:隊列的名字。
- exchange:交換器的名字。
- routingKey:用於綁定的路由鍵。
- arguments:用於綁定的參數
接下來看下它們的源碼。
public AMQP.Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException {
AMQP.Queue.BindOk ok = delegate.queueBind(queue, exchange, routingKey, arguments);
recordQueueBinding(queue, exchange, routingKey, arguments);
return ok;
}
recordQueueBinding(...)
方法如下:
首先要保證隊列的長度小於等於255。
exnWrappingRpc(...)
相當於經過封裝相關屬性,然後返回一個AMQCommand。
- _rpcTimeout:RPC調用的超時時間。
- NO_RPC_TIMEOUT:0。
可以看出privateRpc()
方法分爲兩個步驟。第一是發起僞RPC調用。第二是獲取調用的結果。
發起僞RPC調用
先來看看enqueueRpc(...)
方法。
- _activeRpc:RpcWrapper類型,表示當前的未完成的RPC請求。這裏是
RpcContinuationRpcWrapper
實例。
方法的字面意思是將rpc入隊。實際上一個隊列都沒有用到,只是調用wait()等待。然後再獲取結果。本人感到頗爲意外。
再來看下quiescingTransmit(...)
方法。
- _channelMutex:普通Object對象。
- _blockContent:boolean類型。
- _trafficListener:TrafficListener類型,write(…)方法這裏僅僅是記錄信息。
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。
實際上隊列與交換機的綁定,還是將相關數據寫入到數據流中。
獲取調用的結果
- _blocker:BlockingValueOrException類型。
等待獲取結果直到超時,不響應中斷。
- NANOS_IN_MILLI:1000L * 1000L。
- INFINITY:-1。
等待獲取值。如果已經有值,不需要再等待,返回這個值。達到超時時間,還沒有值到達,會拋出TimeoutException異常。
- INFINITY:-1。
- NANOS_IN_MILLI :1000L * 1000L。
- _filled:表示值還沒有被填充。
- _value:表示被填充的值。