使用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());
然后,创建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);
}
}
结论:
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 类。