activeMQ是一个再apache开源协议下,基于jms编程模型构建的mq消息中间件,本文将从源码角度浅析activeMQ的生产者发送消息实现逻辑。
生产者发送消息的逻辑相对来说比较简单,伪代码如下:
1 public class Producer { 2 private Session session; 3 4 public void send(Message message) { 5 // 通过session找到connection,找到transport,请求broker发送消息 6 session.getConnection().getTransport().request(message); 7 } 8 }
DEMO
了解源码,我们得先找到一个切入口,就从写一个producer发送消息开始,demo如下(抛开注解其实代码也不多):
1 // 构建并配置一个连接工厂 2 // 1、单纯给connectionFactory设置了一下brokerUrl、userName、password 3 // brokerUrl如果有配置参数,会被解析处理并设置到connectionFactory上 4 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); 5 // 创建连接 6 // 1、创建了transport用于发送数据,以及transport启动一个线程轮询数据(带上clientID),如果有监听器,那么触发监听器 7 // 2、transport启动开始,会调用get请求判断能否连接,以及是否支持gzip压缩 8 Connection connection = connectionFactory.createConnection(); 9 // 打开连接 10 // 1、发送连接信息给broker 11 // 2、设置连接的状态为started=true 12 connection.start(); 13 // 创建一个有事务的会话 14 // 1、创建了一个session对象,并关联了connection对象 15 Session session = connection.createSession(true, Session.SESSION_TRANSACTED); 16 // 创建目的地 17 // 1、创建一个queue对象 18 Destination destination = session.createQueue("queue1"); 19 // 创建生产者 20 // 1、构建了一个MessageProducer的实例对象 21 MessageProducer producer = session.createProducer(destination); 22 // 创建消息 23 // 1、创建了一个Message对象 24 TextMessage textMessage = session.createTextMessage(); 25 textMessage.setText("text content"); 26 // 发送消息 27 // 1、开启事务,发送事务信息到broker 28 // 2、发送消息的数据包到broker 29 producer.send(textMessage); 30 // 事务提交 31 // 1、发送数据包,提交事务 32 // 2、发送数据包,回滚事务 33 session.commit(); 34 // 关闭连接 35 // 1、关闭线程池、关闭transport线程 36 connection.close();
我们将从demo入手,至上而下打开代码看看实现逻辑
ConnectionFactory
打开ActiveMQConnectionFactory的构造函数,这里直接使用了默认的,你可以自己指定brokerUrl、userName、password
1 public ActiveMQConnectionFactory() { 2 this(DEFAULT_BROKER_URL); // 调用有参构造函数,默认brokerUrl为:failover://tcp://localhost:61616 3 }
跟进构造函数,最后会调用一个setBrokerURL方法,该方法把brokerUrl配置的jms.前缀的参数给解析出map,然后再通过该map参数去build我们的connectionFactory,最后将brokerUrl设置为connectionFactory的变量就结束了。总得来说,构造connectionFactory就是将brokerUrl的配置转换为ConnectionFactory的配置。
1 Map<String,String> map = URISupport.parseQuery(this.brokerURL.getQuery()); // 解析URI中的配置成为Map 2 Map<String,Object> jmsOptionsMap = IntrospectionSupport.extractProperties(map, "jms."); // 提取前缀为jms.的配置,例如jms.name -> name 3 // 将map中的配置设置并移除 4 if (buildFromMap(jmsOptionsMap)) { 5 // 如果还有配置剩下,那么是非法配置 6 if (!jmsOptionsMap.isEmpty()) { 7 String msg = "There are " + jmsOptionsMap.size() 8 + " jms options that couldn't be set on the ConnectionFactory." 9 + " Check the options are spelled correctly." 10 + " Unknown parameters=[" + jmsOptionsMap + "]." 11 + " This connection factory cannot be started."; 12 throw new IllegalArgumentException(msg); 13 } 14 // 重新设置brokerUrl 15 this.brokerURL = URISupport.createRemainingURI(this.brokerURL, map); // 将剩下的参数拼接到brokerUrl上 16 }
createConnection
进入ActiveMQConnectionFactory的createConnection方法
1 ActiveMQConnection connection = null; 2 try { 3 Transport transport = createTransport(); // 创建transport对象,如果是http的schema,那么创建HttpClientTransport 4 connection = createActiveMQConnection(transport, factoryStats); // 创建连接对象 5 6 connection.setUserName(userName); // 设置用户名 7 connection.setPassword(password); // 设置密码 8 9 configureConnection(connection); // 配置连接 10 11 transport.start(); // 启动transport 12 13 if (clientID != null) { 14 connection.setDefaultClientID(clientID); 15 } 16 17 return connection; // 创建成功 18 } catch (JMSException e) { 19 // ... 20 }
逻辑比较简单就是创建了一个Connection的实例对象,关注点在于创建了一个Transport,后面调用了Transport的start方法。
Transport负责的是与broker通信,如果是HTTP通信,那么可以简单理解为向broker发送了HTTP请求。start方法启动了一个线程去做消费者相关的事,本文暂时先不讨论消费者部分。
start
进入ActiveMQConnection的start方法,该方法将connection的状态通过cas操作设置为start=true,然后遍历connection下包含的session,调用start方法,一开始的时候session还未创建,所以将不执行任何session.start操作
1 if (started.compareAndSet(false, true)) { // 设置连接为start 2 for (Iterator<ActiveMQSession> i = sessions.iterator(); i.hasNext();) { // 如果存在已经创建的session,那么启动session 3 ActiveMQSession session = i.next(); 4 session.start(); 5 } 6 }
createSession
进入ActiveMQConnection的createSession方法,该方法创建了一个ActiveMQSession实例对象
1 if (!transacted) { // 不开启事务 2 // ...... 3 } 4 return new ActiveMQSession(this, getNextSessionId(), transacted ? Session.SESSION_TRANSACTED : acknowledgeMode, isDispatchAsync(), isAlwaysSessionAsync());
这里我们打开构造方法看看,核心点在于它构造了一个TransactionContext事务上下文,后面开启事务相关将会用到(所以事务控制是由session来负责的)
然后当前session会被加入到connection的集合中维护,另外如果connection已经创建了,那么当前session也直接调用start方法修改状态started=true
1 // ... 2 setTransactionContext(new TransactionContext(connection)); // 构建一个TransactionContext,包含connection 3 // ... 4 connection.addSession(this); // 将当前session放入到connection中,copyOnWriteArrayList的容器中 5 if (connection.isStarted()) { 6 start(); // 如果connection已经start,那么当前session立即start 7 }
createQueue
进入ActiveMQSession的createQueue方法,就是简单地创建了一个对象
1 // ... 2 return new ActiveMQQueue(queueName);
createProducer
进入ActiveMQSession的createProducer方法,就是创建一个MessageProducer实例对象
1 // ... 2 int timeSendOut = connection.getSendTimeout(); 3 return new ActiveMQMessageProducer(this, getNextProducerId(), ActiveMQMessageTransformation.transformDestination(destination),timeSendOut); // 构建一个producer实例,关联session
createTextMessage
进入ActiveMQSession地createTextMessage方法,一样是创建实例对象
1 ActiveMQTextMessage message = new ActiveMQTextMessage(); 2 configureMessage(message); 3 return message;
send
进入ActiveMQMessageProducer的send方法,这个方法很长,核心代码就一句
1 this.session.send(this, dest, message, deliveryMode, priority, timeToLive, producerWindow, sendTimeout, onComplete); // 由session执行send操作
所以,我们进到ActiveMQSession的send方法看看,该方法先是开启了事务,然后把msg通过调用connection的Transport的来发送给broker
1 synchronized (sendMutex) { 2 doStartTransaction(); // 开启事务,发送给broker事务信息 3 // ... 4 if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() && !connection.isAlwaysSyncSend() && (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) { 5 this.connection.asyncSendPacket(msg); // 发送数据包 6 // ... 7 } else { 8 if (sendTimeout > 0 && onComplete==null) { 9 this.connection.syncSendPacket(msg,sendTimeout); 10 }else { 11 this.connection.syncSendPacket(msg, onComplete); 12 } 13 } 14 15 }
我们看看doStartTransaction开启事务干了啥,首先是判断transacted=true,然后得排除掉当前事务处于XA协议分布式事务里
1 if (getTransacted() && !transactionContext.isInXATransaction()) { // 开启了事务,且没有加入XA事务当中 2 transactionContext.begin(); // 开启事务 3 }
再看看begin干了啥,其实就是发送了begin事务信息给broker
1 if (transactionId == null) { 2 // ... 3 TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.BEGIN); 4 // ... 5 this.connection.asyncSendPacket(info); // 发送事务信息给broker 6 // ... 7 }
commit
前面我们开启了事务,并发送了事务信息给broker,现在我们做完了,需要将事务提交,打开ActiveMQSession的commit方法,调用了transactionContext的commit方法
1 // ... 2 transactionContext.commit();
再跟进,我们看到如果需要回滚则调用rollback回滚,把rollback,否则把commit的事务信息发送给broker。
1 try { 2 beforeEnd(); 3 } catch (JMSException e) { 4 rollback(); 5 throw e; 6 } 7 8 if (transactionId != null && rollbackOnly) { 9 // ... 10 try { 11 rollback(); 12 } finally { 13 // 。。。 14 } 15 } 16 17 // Only send commit if the transaction was started. 18 if (transactionId != null) { 19 // ... 20 21 TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.COMMIT_ONE_PHASE); 22 // ... 23 // Notify the listener that the tx was committed back 24 try { 25 this.connection.syncSendPacket(info); // 发送数据包,提交事务 26 // ... 27 } catch (JMSException cause) { 28 // ... 29 } 30 31 }
总结
ActiveMQ的Producer发送消息部分相对来说比较简单,基本上就是通过Transport与broker进行请求应答的操作。而事务部分没有采用XA两阶段协议,只是采用了一阶段协议。