1. 環境搭建
-
代碼已經上傳至 https://github.com/masteryourself/activemq.git ,分支名稱是
masteryourself-activemq-5.15.9
-
producer 是
activemq-example
工程,啓動類是QueueProducer
2. 源碼解析
2.1 流程預覽
// 1. 創建 ActiveMQConnection
org.apache.activemq.ActiveMQConnectionFactory#createConnection ->
org.apache.activemq.ActiveMQConnectionFactory#createActiveMQConnection
// 1.1(*) 創建 transport,用於與 server 端進行交互
org.apache.activemq.ActiveMQConnectionFactory#createTransport ->
org.apache.activemq.transport.TransportFactory#connect ->
// 1.1.1(*) 這裏是 spi 擴展,和 dubbo 類似
// 從 META-INF/services/org/apache/activemq/transport/tcp 文件中找到實現類【TcpTransportFactory】
org.apache.activemq.transport.TransportFactory#findTransportFactory
// 1.1.2(*) 調用 doConnect 方法,返回 Transport 對象
org.apache.activemq.transport.TransportFactory#doConnect
// 1.1.2.1 創建 Transport
org.apache.activemq.transport.tcp.TcpTransportFactory#createTransport->
// tcpTransport 裏持有 socketFactory 對象,socketFactory 會創建一個 socket,所以 tcpTransport 就是操作 socket
org.apache.activemq.transport.tcp.TcpTransportFactory#createTcpTransport
// 1.1.2.2(*) 配置 configure,這個裏面是對 Transport 做鏈路包裝
org.apache.activemq.transport.TransportFactory#configure
// 1.1.2.2.1 組裝一個複合的transport,這裏會包裝兩層,一個是 IactivityMonitor.另一個是 WireFormatNegotiator
// WireFormatNegotiator 實現了客戶端連接 broker 的時候先發送數據解析相關的協議信息,比如解析版本號,是否使用緩存等
// InactivityMonitor 用於實現連接成功成功後的心跳檢查機制,客戶端每 10s 發送一次心跳信息。服務端每 30s 讀取一次心跳信息
org.apache.activemq.transport.tcp.TcpTransportFactory#compositeConfigure
// 1.1.2.2.2 用 MutexTransport 包裝,實現寫鎖,表示同一時間只允許發送一個請求
org.apache.activemq.transport.MutexTransport#<init>
// 1.1.2.2.3 用 ResponseCorrelator 包裝,用於實現異步請求
org.apache.activemq.transport.ResponseCorrelator#<init>
// 1.2(*) 設置 ActiveMQConnection 的屬性,如 prefetchPolicy、optimizedMessageDispatch 等屬性
org.apache.activemq.ActiveMQConnectionFactory#configureConnection
// 1.3(*) 最終會啓動 TransportThreadSupport 的
org.apache.activemq.transport.TransportFilter#start ->
// 這裏省略了一些一些包裝類,最終會調用 ServiceSupport 中的 start 方法
org.apache.activemq.util.ServiceSupport#start ->
// 1.3.1 利用 socketFactory 創建 socket 對象(連接 activeMQ server 端)
org.apache.activemq.transport.tcp.TcpTransport#connect
// 1.3.2 調用 TcpTransport 中的 doStart 方法
org.apache.activemq.transport.tcp.TcpTransport#doStart ->
// 啓動線程,runnable 對象是 【TcpTransport】,所以在 【TcpTransport】 的 run 方法中等待結果
org.apache.activemq.transport.TransportThreadSupport#doStart
// 2. 【TcpTransport】 中的 run 方法,空殼方法,調用 doRun
org.apache.activemq.transport.tcp.TcpTransport#run ->
// 調用 doConsume 處理
org.apache.activemq.transport.tcp.TcpTransport#doRun
// 2.1 從 socket 讀數據,封裝成 command
org.apache.activemq.transport.tcp.TcpTransport#readCommand
// 2.2 Transport 的包裝類處理邏輯
org.apache.activemq.transport.TransportSupport#doConsume ->
// Transport 的包裝類處理邏輯
org.apache.activemq.transport.AbstractInactivityMonitor#onCommand ->
// Transport 的包裝類處理邏輯
org.apache.activemq.transport.WireFormatNegotiator#onCommand ->
// Transport 的包裝類處理邏輯
org.apache.activemq.transport.MutexTransport#onCommand ->
// Transport 的包裝類處理邏輯
org.apache.activemq.transport.ResponseCorrelator#onCommand ->
// ActiveMQ 在這裏處理來自於 server 端的 command 命令
org.apache.activemq.ActiveMQConnection#onCommand
2.2 流程詳解
2.2.1 ActiveMQConnectionFactory#createTransport(1.1)
org.apache.activemq.ActiveMQConnectionFactory
protected Transport createTransport() throws JMSException {
try {
// 即 tcp://192.168.89.210:61616
URI connectBrokerUL = brokerURL;
String scheme = brokerURL.getScheme();
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + brokerURL + "]");
}
if (scheme.equals("auto")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto", "tcp"));
} else if (scheme.equals("auto+ssl")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+ssl", "ssl"));
} else if (scheme.equals("auto+nio")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio", "nio"));
} else if (scheme.equals("auto+nio+ssl")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio+ssl", "nio+ssl"));
}
// 根據 scheme 動態構建一個 Transport
return TransportFactory.connect(connectBrokerUL);
} catch (Exception e) {
throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e);
}
}
2.2.2 TransportFactory#findTransportFactory(1.1.1)
org.apache.activemq.transport.TransportFactory
public static TransportFactory findTransportFactory(URI location) throws IOException {
String scheme = location.getScheme();
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + location + "]");
}
TransportFactory tf = TRANSPORT_FACTORYS.get(scheme);
if (tf == null) {
// Try to load if from a META-INF property.
try {
// 這裏是 spi 擴展,和 dubbo 類似
// 從 META-INF/services/org/apache/activemq/transport/tcp 文件中找到實現類【TcpTransportFactory】
tf = (TransportFactory)TRANSPORT_FACTORY_FINDER.newInstance(scheme);
// 放置到緩存中
TRANSPORT_FACTORYS.put(scheme, tf);
} catch (Throwable e) {
throw IOExceptionSupport.create("Transport scheme NOT recognized: [" + scheme + "]", e);
}
}
return tf;
}
2.2.3 TransportFactory#doConnect(1.1.2)
org.apache.activemq.transport.TransportFactory
public Transport doConnect(URI location) throws Exception {
try {
Map<String, String> options = new HashMap<String, String>(URISupport.parseParameters(location));
if( !options.containsKey("wireFormat.host") ) {
options.put("wireFormat.host", location.getHost());
}
WireFormat wf = createWireFormat(options);
// 創建一個Transport,就是創建一個 socket連接
Transport transport = createTransport(location, wf);
// 配置 configure,這個裏面是對 Transport 做鏈路包裝
Transport rc = configure(transport, wf, options);
//remove auto
IntrospectionSupport.extractProperties(options, "auto.");
if (!options.isEmpty()) {
throw new IllegalArgumentException("Invalid connect parameters: " + options);
}
return rc;
} catch (URISyntaxException e) {
throw IOExceptionSupport.create(e);
}
}
2.2.4 TransportFactory#configure(1.1.2.2)
org.apache.activemq.transport.TransportFactory
public Transport configure(Transport transport, WireFormat wf, Map options) throws Exception {
// 組裝一個複合的transport,這裏會包裝兩層,一個是 IactivityMonitor.另一個是 WireFormatNegotiator
// WireFormatNegotiator 實現了客戶端連接 broker 的時候先發送數據解析相關的協議信息,比如解析版本號,是否使用緩存等
// InactivityMonitor 用於實現連接成功成功後的心跳檢查機制,客戶端每 10s 發送一次心跳信息。服務端每 30s 讀取一次心跳信息
transport = compositeConfigure(transport, wf, options);
// 再做一層包裝 MutexTransport,實現寫鎖,表示同一時間只允許發送一個請求
transport = new MutexTransport(transport);
// 包裝 ResponseCorrelator,用於實現異步請求
transport = new ResponseCorrelator(transport);
return transport;
}