什麼是持久化消息?
保證消息只被傳送一次和成功使用一次。在持久性消息傳送至目標時,消息服務將其放入持久性數據存儲。如果消息服務由於某種原因導致失敗,它可以恢復此消息並將此消息傳送至相應的消費者。雖然這樣增加了消息傳送的開銷,但卻增加了可靠性。
1.消息可靠性之持久化
1.1 queue消息非持久和持久
隊列默認是持久化的
// 非持久化
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 持久化
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
public class JmsProduce {
public static final String ACTIVEMQ_URL = "tcp://118.24.20.3:61626";
public static final String QUEUE_NAME = "jdbc01";
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_NAME);
MessageProducer messageProducer = session.createProducer(queue);
// 非持久化
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
for (int i = 1; i < 4 ; i++) {
TextMessage textMessage = session.createTextMessage("---MessageListener---" + i);
messageProducer.send(textMessage);
}
messageProducer.close();
session.close();
connection.close();
System.out.println(" **** 消息發送到MQ完成 ****");
}
}
1.2 Topic消息非持久和持久
topic默認就是非持久化的,因爲生產者生產消息時,消費者也要在線,這樣消費者才能消費到消息。
topic消息持久化,只要消費者向MQ服務器註冊過,所有生產者發佈成功的消息,該消費者都能收到,不管是MQ服務器宕機還是消費者不在線。
注意:
1.一定要先運行一次消費者,等於向MQ註冊,類似我訂閱了這個主題。
2.然後再運行生產者發送消息。
3.之後無論消費者是否在線,都會收到消息。如果不在線的話,下次連接的時候,會把沒有收過的消息都接收過來。
// 持久化topic 的消息生產者
public class JmsProduce_persistence {
public static final String ACTIVEMQ_URL = "tcp://192.168.17.3:61616";
public static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
javax.jms.Connection connection = activeMQConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
MessageProducer messageProducer = session.createProducer(topic);
// 設置持久化topic
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
// 設置持久化topic之後再,啓動連接
connection.start();
for (int i = 1; i < 4 ; i++) {
TextMessage textMessage = session.createTextMessage("topic_name--" + i);
messageProducer.send(textMessage);
MapMessage mapMessage = session.createMapMessage();
}
messageProducer.close();
session.close();
connection.close();
System.out.println(" **** TOPIC_NAME消息發送到MQ完成 ****");
}
}
// 持久化topic 的消息消費者
public class JmsConsummer_persistence {
public static final String ACTIVEMQ_URL = "tcp://192.168.17.3:61616";
public static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
// 設置客戶端ID。向MQ服務器註冊自己的名稱
connection.setClientID("marrry");
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
// 創建一個topic訂閱者對象。一參是topic,二參是訂閱者名稱
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic,"remark...");
// 之後再開啓連接
connection.start();
Message message = topicSubscriber.receive();
while (null != message){
TextMessage textMessage = (TextMessage)message;
System.out.println(" 收到的持久化 topic :"+textMessage.getText());
message = topicSubscriber.receive();
}
session.close();
connection.close();
}
}
控制界面顯示:
2.消息可靠性之事務
(1)生產者開啓事務後,執行commit方法,這批消息才真正的被提交。不執行commit方法,這批消息不會提交。執行rollback方法,之前的消息會回滾掉。生產者的事務機制,要高於簽收機制,當生產者開啓事務,簽收機制不再重要。
(2)消費者開啓了事務就必須手動提交,不然會重複消費消息
// 創建會話session,兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
// 消費者開啓了事務就必須手動提交,不然會重複消費消息
final Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
session.commit();
3.消息可靠性之簽收
1. 自動簽收(Session.AUTO_ACKNOWLEDGE):該方式是默認的。該種方式,無需我們程序做任何操作,框架會幫我們自動簽收收到的消息。
2.手動簽收(Session.CLIENT_ACKNOWLEDGE):手動簽收。該種方式,需要我們手動調用Message.acknowledge(),來簽收消息。如果不簽收消息,該消息會被我們反覆消費,只到被簽收。
- 開啓了事務的情況下,開啓了手動簽收
- connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);,如果沒有提交事務,沒有執行commit(),就算執行了應答textMessage.acknowledge(),還是會重複消費。
- 如果執行了commit,沒有進行手動應答textMessage.acknowledge(),不會產生重複消費。
4.消息持久化機制之JDBC
4.1開啓docker 中的activemq,並且進入到activeMq的文件目錄中
4.2將mysql -connector -javajar包導入到容器內的lib
4.3jdbcPersistenceAdapter配置 修改配置文件activemq.xml。將之前的替換爲jdbc的配置。如下:
<!--
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
-->
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#mysql-ds" createTableOnStartup="true"/>
</persistenceAdapter>
4.4數據庫連接池配置 需要我們準備一個mysql數據庫,並創建一個名爲activemq的數據庫。新建的數據庫要採用latin1 或者ASCII編碼
在 < /broker > 標籤和< import>標籤之間插入數據庫連接池配置
</broker>
<bean id="mysql-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:// 192.168.40.1/activemq?relaxAutoCommit=true"/>
<property name="username" value="activemq"/>
<property name="password" value="123456"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
<import resource="jetty.xml"/>
ACTIVEMQ_MSGS數據表:
ACTIVEMQ_ACKS數據表:
ACTIVEMQ_LOCK數據表:
5.消息持久化之JDBC with ActiveMQ Journal
這種方式克服了JDBC Store的不足,JDBC每次消息過來,都需要去寫庫讀庫。ActiveMQ Journal,使用高速緩存寫入技術,大大提高了性能。當消費者的速度能夠及時跟上生產者消息的生產速度時,journal文件能夠大大減少需要寫入到DB中的消息。
舉個例子:生產者生產了1000條消息,這1000條消息會保存到journal文件,如果消費者的消費速度很快的情況下,在journal文件還沒有同步到DB之前,消費者已經消費了90%的以上消息,那麼這個時候只需要同步剩餘的10%的消息到DB。如果消費者的速度很慢,這個時候journal文件可以使消息以批量方式寫到DB。
爲了高性能,這種方式使用日誌文件存儲+數據庫存儲。先將消息持久到日誌文件,等待一段時間再將未消費的消息持久到數據庫。該方式要比JDBC性能要高。
5.1 配置
6.總結
① jdbc效率低,kahaDB效率高,jdbc+Journal效率較高。
② 持久化消息主要指的是:MQ所在服務器宕機了消息不會丟試的機制。
③ 持久化機制演變的過程:
從最初的AMQ Message Store方案到ActiveMQ V4版本退出的High Performance Journal(高性能事務支持)附件,並且同步推出了關於關係型數據庫的存儲方案。ActiveMQ5.3版本又推出了對KahaDB的支持(5.4版本後被作爲默認的持久化方案),後來ActiveMQ 5.8版本開始支持LevelDB,到現在5.9提供了標準的Zookeeper+LevelDB集羣化方案。