在介紹activemq之前,先簡單介紹JMS,它是J2EE的13個規範之一,提供的是消息中間件的規範。
JMS包括以下基本構件:
連接工廠,是客戶用來創建連接的對象,ActiveMQ提供的是ActiveMQConnectionFactory;
連接connection;
會話session,是發送和接收消息的上下文,用於創建消息生產者,消息消費者,相比rocketMQ會話session是提供事務性的;
目的地destination,指定生產消息的目的地和消費消息的來源對象;
生產者、消費者,由會話創建的對象,顧名思義。
消息通信機制
點對點模式,每個消息只有1個消費者,它的目的地稱爲queue隊列;
發佈/訂閱模式,每個消息可以有多個消費者,而且訂閱一個主題的消費者,只能消費自它訂閱之後發佈的消息。
消息確認機制
Session.AUTO_ACKNOWLEDGE,直接使用receive方法。
Session.CLIENT_ACKNOWLEDGE,通過消息的acknowledge 方法確認消息。
Session.DUPS_ACKNOWLEDGE,該選擇只是會話遲鈍第確認消息的提交。如果JMS provider 失敗,那麼可能會導致一些重複的消息。如果是重複的消息,那麼JMS provider 必須把消息頭的JMSRedelivered 字段設置爲true。
package com.java.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息生產者
* @author xing.liu
*
*/
public class JMSProducer {
private static final String USERNAME=ActiveMQConnection.DEFAULT_USER; // 默認的連接用戶名
private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD; // 默認的連接密碼
private static final String BROKEURL=ActiveMQConnection.DEFAULT_BROKER_URL; // 默認的連接地址
private static final int SENDNUM=10; // 發送的消息數量
public static void main(String[] args) {
ConnectionFactory connectionFactory; // 連接工廠
Connection connection = null; // 連接
Session session; // 會話 接受或者發送消息的線程
Destination destination; // 消息的目的地
MessageProducer messageProducer; // 消息生產者
// 實例化連接工廠
connectionFactory=new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL);
try {
connection=connectionFactory.createConnection(); // 通過連接工廠獲取連接
connection.start(); // 啓動連接
// 創建Session,第一個參數是開啓事務true,第二個參數是消息確認機制屬性
session=connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
destination=session.createQueue("FirstQueue1"); // 創建消息隊列
messageProducer=session.createProducer(destination); // 創建消息生產者
sendMessage(session, messageProducer); // 發送消息
session.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 發送消息
* @param session
* @param messageProducer
* @throws Exception
*/
public static void sendMessage(Session session,MessageProducer messageProducer)throws Exception{
for(int i=0;i<JMSProducer.SENDNUM;i++){
TextMessage message=session.createTextMessage("ActiveMQ 發送的消息"+i);
System.out.println("發送消息:"+"ActiveMQ 發送的消息"+i);
messageProducer.send(message);
}
}
}
原理很多,但是把原理映射在實踐中,就更容易懂了。一起看看下面的這個小例子,通過代碼回想原理,最後運行顯示到控制檯上,驗證結論。
新建一個普通Java Project,引進activemq的jar包,build到項目中。新建生產者producer、消費者consumer,如下:
package com.java.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息消費者
* @author xing.liu
*
*/
public class JMSConsumer {
private static final String USERNAME=ActiveMQConnection.DEFAULT_USER; // 默認的連接用戶名
private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD; // 默認的連接密碼
private static final String BROKEURL=ActiveMQConnection.DEFAULT_BROKER_URL; // 默認的連接地址
public static void main(String[] args) {
ConnectionFactory connectionFactory; // 連接工廠
Connection connection = null; // 連接
Session session; // 會話 接受或者發送消息的線程
Destination destination; // 消息的目的地
MessageConsumer messageConsumer; // 消息的消費者
// 實例化連接工廠
connectionFactory=new ActiveMQConnectionFactory(JMSConsumer.USERNAME, JMSConsumer.PASSWORD, JMSConsumer.BROKEURL);
try {
connection=connectionFactory.createConnection(); // 通過連接工廠獲取連接
connection.start(); // 啓動連接
session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); // 創建Session
destination=session.createQueue("FirstQueue1"); // 創建連接的消息隊列
messageConsumer=session.createConsumer(destination); // 創建消息消費者
while(true){
TextMessage textMessage=(TextMessage)messageConsumer.receive(100000);
if(textMessage!=null){
System.out.println("收到的消息:"+textMessage.getText());
}else{
break;
}
}
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
這個demo是簡單的點對點模式下,目的地對象是queue隊列,代碼中可以看出session.createQueue;從代碼中可以聯想到上邊的原理,也是簡而易懂。messageConsumer.receive(100000)這使用的是receive模式,這種模式讓消費者consumer不斷的問producer要消息,很亂的。
安裝了ActiveMQ服務器,在瀏覽器打開後臺監控,地址 http://127.0.0.1:8161/admin/
可以在Queues中看到消息未被消費的條數,已消費的條數。後臺監控系統都是一樣的,類似rocketMQ,我就不介紹了,多點點就能明白。
所以生產中我們常用的是監聽listener模式,看看下邊這個帶監聽的consumer:
package com.java.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息消費者
* @author xing
*
*/
public class JMSConsumer2 {
private static final String USERNAME=ActiveMQConnection.DEFAULT_USER; // 默認的連接用戶名
private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD; // 默認的連接密碼
private static final String BROKEURL=ActiveMQConnection.DEFAULT_BROKER_URL; // 默認的連接地址
public static void main(String[] args) {
ConnectionFactory connectionFactory; // 連接工廠
Connection connection = null; // 連接
Session session; // 會話 接受或者發送消息的線程
Destination destination; // 消息的目的地
MessageConsumer messageConsumer; // 消息的消費者
// 實例化連接工廠
connectionFactory=new ActiveMQConnectionFactory(JMSConsumer2.USERNAME, JMSConsumer2.PASSWORD, JMSConsumer2.BROKEURL);
try {
connection=connectionFactory.createConnection(); // 通過連接工廠獲取連接
connection.start(); // 啓動連接
session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); // 創建Session
destination=session.createQueue("FirstQueue1"); // 創建連接的消息隊列
messageConsumer=session.createConsumer(destination); // 創建消息消費者
messageConsumer.setMessageListener(new Listener()); // 註冊消息監聽
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.java.activemq;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* 消息監聽
* @author xing
*
*/
public class Listener implements MessageListener{
@Override
public void onMessage(Message message) {
try {
System.out.println("收到的消息:"+((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
這種消息監聽模式,只是多一個註冊消息監聽messageConsumer.setMessageListener(new Listener());