01.ActiveMQ 源碼解析之創建連接

1. 環境搭建

  • 代碼已經上傳至 https://github.com/masteryourself/activemq.git ,分支名稱是 masteryourself-activemq-5.15.9

  • producer 是 activemq-example 工程,啓動類是 QueueProducer

2. 源碼解析

2.1 流程預覽

image

// 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章