對於queue的方式做到持久化很簡單。每個消息只有一個消費者,只要將消息存入數據庫,然後消費者取走信息即可,在這裏我們討論的是topic的持久化方式。
1.在這裏我用的是oracle數據庫,先將oracle的驅動包放入lib文件夾下,再配置activemq.xml文件中的配置信息
將原來的默認持久化方式改爲使用jdbc的方式:
<!--配置JDBC適配器:-->
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#oracle-ds" useDatabaseLock="false"/>
</persistenceAdapter>
配置oracle-ds的bean,注意須將bean配置在broker節點之外
<bean id="oracle-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
<property name="maxTotal" value="200"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
生產者
package com.yc.mytest;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
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;
public class MyProducer {
private static final String URL=ActiveMQConnection.DEFAULT_BROKER_URL; //默認的連接地址
private static String TOPIC_NAME="MyTopicTest"; //topic名字
public static void main(String[] args) {
ConnectionFactory factory; //連接工廠
Connection conn = null; //連接
Session session; //會話
Destination dest; //消息的目的地
MessageProducer producer; //消息生產者
try {
//初始化連接工廠
factory=new ActiveMQConnectionFactory(URL);
//獲取連接
conn=factory.createConnection();
//開始連接
conn.start();
//創建Session,此方法第一個參數表示會話是否在事務中執行,第二個參數設定會話的應答模式
session=conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//創建topic,producer和consumer將根據TOPIC_NAME來發送/接收對應的消息
dest=session.createTopic(TOPIC_NAME);
//創建生產者
producer=session.createProducer(dest);
//設置持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//發送消息
sendMessage(session, producer);
} catch (Exception e) {
System.out.println("出錯啦!!!");
e.printStackTrace();
} finally{
if(conn!=null){
try {
//關閉連接
conn.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
private static void sendMessage(Session session,MessageProducer producer) throws JMSException{
//初始化一個mq消息
TextMessage message = session.createTextMessage() ;//= session.createTextMessage("你好 ActiveMq " + i);
//填寫消息內容
message.setText("你好 ActiveMq");
//發送消息
producer.send(message);
System.out.println("消息發送成功!!!");
}
}
package com.yc.mytest;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MyConsumer implements MessageListener {
private static final String URL=ActiveMQConnection.DEFAULT_BROKER_URL; //默認的連接地址
private static String TOPIC_NAME="MyTopicTest"; //topic名字
public static void main(String[] args) {
ConnectionFactory factory; //連接工廠
Connection conn = null; //連接
Session session; //會話
Destination dest; //消息的目的地
MessageConsumer consumer; //消費者
try {
//初始化連接工廠
factory=new ActiveMQConnectionFactory(URL);
//獲取連接
conn=factory.createConnection();
conn.setClientID("C1");
//開始連接
conn.start();
//創建Session,此方法第一個參數表示會話是否在事務中執行,第二個參數設定會話的應答模式
session=conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//創建topic,producer和consumer將根據TOPIC_NAME來發送/接收對應的消息
dest=session.createTopic(TOPIC_NAME);
//創建消費者
//consumer=session.createConsumer(dest);
//創建消費者(持久化)
consumer = session.createDurableSubscriber((Topic) dest, TOPIC_NAME);
//初始化MessageListener
MyConsumer me=new MyConsumer();
//給消費者設定監聽對象
consumer.setMessageListener(me);
} catch (Exception e) {
System.out.println("出錯啦!!!");
e.printStackTrace();
}
}
public void onMessage(Message message) {
TextMessage txtMessage = (TextMessage)message;
String text;
try {
text=txtMessage.getText();
//txtMessage.acknowledge(); //通知服務器
System.out.println("收到消息:"+text);
} catch (JMSException e) {
System.out.println("出錯啦!!!");
e.printStackTrace();
}
}
}
在這裏我們可以編寫兩個消費者模擬羣發的測試,須要對conn.setClient()設置不同的id。筆者使用了C1與C2,由於代碼相同,我就不貼C2的代碼了。
3.分析與測試
在使用jdbc的方式實現持久化的時候,activemq會在數據庫創建三張表,ACTIVEMQ_MSGS,ACTIVEMQ_ACKS和ACTIVEMQ_LOCK。第二張表外鍵關聯到第一張表,共同存儲消息,第三張表用於鎖定保證只有一個broker實例可以訪問數據庫。
測試:
1.先啓動MyConsumer類,讓MyConsumer在自己的主題上進行訂閱
2.停止MyConsumer類,再啓動MyProducer類
3.待MyProducer類運行完成後,再啓動MyConsumer類
4.發現數據庫中存在信息,相應主題的信息被訂閱,證明持久化成功。