漫遊MQ-socket 長連線

炸雞店老闆發表於2017-10-28

使用MQ傳送或者接收訊息,首先需要與broker 保持通訊,是怎樣工作的?

consumer繫結的listener是什麼時機觸發的?


Insight 過程使用的activemq,模板如下:

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();

Topic topic = session.createTopic("log-topic");
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new LogListener());
建立connection,基於TansportFactory.createTransport() 會建立FailoverTransport,保障mq通訊failover。

然後,建立session,也就是執行緒ActiveMQ Task-n 會建立關鍵的TcpTransport, 通過FailoverTransport.task 的iterate方法,開始建立連線。

TcpTransport 與broker建立了長連線,同時實現了Runnable介面,訊息的傳遞以及listener的觸發就看run() 的實現了。

過程如下:socket讀取-listener消費鏈處理-對應的session分發message- message 入佇列。

由上述的過程可以看到,consumer繫結的listener並沒有直接通知消費,接著看程式碼。

session建立的taskRunner,呼叫wakeup()後,啟動消費message佇列的執行緒,如果有message 入佇列,則分發至對應的consumer,具體的處理流程見原始碼:

public void dispatch(MessageDispatch md) {
    MessageListener listener = this.messageListener.get();
    try {
    	synchronized (unconsumedMessages.getMutex()) {
            if (!unconsumedMessages.isClosed()) {
                if (listener != null && unconsumedMessages.isRunning()) {
                	ActiveMQMessage message = createActiveMQMessage(md);
                	beforeMessageIsConsumed(md);
                	try {
                		boolean expired = message.isExpired();
                		if (!expired) {
                		    listener.onMessage(message);
                		}
                		afterMessageIsConsumed(md, expired);
                	} catch (RuntimeException e) {
                	    // ack rollback...
                	}
                } else {
                	unconsumedMessages.enqueue(md);
                	if (availableListener != null) {
                		availableListener.onMessageAvailable(this);
                	}
                }
            }
    	}
    } catch (Exception e) {
        session.connection.onClientInternalException(e);
    }
}
可以對比看 receive() 的處理。


結論:

mq 的通訊預設使用tcp socket 長連線實現,支援failover。

consumer繫結的listener由session 級別的執行緒去觸發,沒有listener 則將message轉入unconsume佇列,待呼叫receive()再消費該message。


備註

mq封裝實現的自己的Executor用於長連線任務的管理,TaskRunnerFactory name = "ActiveMQ Task",真正的提交runnable task其實還是ThreadPoolExecutor去完成。

生成全域性唯一的Id,可以參考IdGenerator。

mq Command消費的listener傳遞鏈,可以參考TransportFilter 類。


相關文章