activeMQ(一)生产消息源码解析

activeMQ是一个再apache开源协议下,基于jms编程模型构建的mq消息中间件,本文将从源码角度浅析activeMQ的实现逻辑。

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两阶段协议,只是采用了一阶段协议。

猜你喜欢

转载自www.cnblogs.com/lay2017/p/11094374.html
今日推荐