ActiveMQ持久化方式
訊息永續性對於可靠訊息傳遞來說應該是一種比較好的方法,有了訊息持久化,即使傳送者和接受者不是同時線上或者訊息中心在傳送者傳送訊息後當機了,在訊息中心重新啟動後仍然可以將訊息傳送出去,如果把這種持久化和ReliableMessaging結合起來應該是很好的保證了訊息的可靠傳送。
訊息永續性的原理很簡單,就是在傳送者將訊息傳送出去後,訊息中心首先將訊息儲存到本地資料檔案、記憶體資料庫或者遠端資料庫等,然後試圖將訊息傳送給接收者,傳送成功則將訊息從儲存中刪除,失敗則繼續嘗試。訊息中心啟動以後首先要檢查制定的儲存位置,如果有未傳送成功的訊息,則需要把訊息傳送出去。
ActiveMQ持久化方式:AMQ、KahaDB、JDBC、LevelDB。
1、AMQ
AMQ是一種檔案儲存形式,它具有寫入速度快和容易恢復的特點。訊息儲存在一個個檔案中,檔案的預設大小為32M,如果一條訊息的大小超過了32M,那麼這個值必須設定大一點。當一個儲存檔案中的訊息已經全部被消費,那麼這個檔案將被標識為可刪除,在下一個清除階段,這個檔案被刪除。AMQ適用於ActiveMQ5.3之前的版本。預設配置如下:
- <persistenceAdapter>
- <amqPersistenceAdapter directory="activemq-data"maxFileLength="32mb"/>
- </persistenceAdapter>
屬性如下:
屬性名稱 |
預設值 |
描述 |
directory |
activemq-data |
訊息檔案和日誌的儲存目錄 |
useNIO |
true |
使用NIO協議儲存訊息 |
syncOnWrite |
false |
同步寫到磁碟,這個選項對效能影響非常大 |
maxFileLength |
32Mb |
一個訊息檔案的大小 |
persistentIndex |
true |
訊息索引的持久化,如果為false,那麼索引儲存在記憶體中 |
maxCheckpointMessageAddSize |
4kb |
一個事務允許的最大訊息量 |
cleanupInterval |
30000 |
清除操作週期,單位ms |
indexBinSize |
1024 |
索引檔案快取頁面數,預設為1024,當amq擴充或者縮減儲存時,會鎖定整個broker,導致一定時間的阻塞,所以這個值應該調整到比較大,但是程式碼中實現會動態伸縮,調整效果並不理想。 |
indexKeySize |
96 |
索引key的大小,key是訊息ID |
indexPageSize |
16kb |
索引的頁大小 |
directoryArchive |
archive |
儲存被歸檔的訊息檔案目錄 |
archiveDataLogs |
false |
當為true時,歸檔的訊息檔案被移到directoryArchive,而不是直接刪除 |
2、KahaDB
KahaDB是基於檔案的本地資料庫儲存形式,雖然沒有AMQ的速度快,但是它具有強擴充套件性,恢復的時間比AMQ短,從5.4版本之後KahaDB做為預設的持久化方式。預設配置如下:
- <persistenceAdapter>
- <kahaDB directory="activemq-data"journalMaxFileLength="32mb"/>
- </persistenceAdapter>
KahaDB的屬性如下:
屬性名稱 |
預設值 |
描述 |
directory |
activemq-data |
訊息檔案和日誌的儲存目錄 |
indexWriteBatchSize |
1000 |
一批索引的大小,當要更新的索引量到達這個值時,更新到訊息檔案中 |
indexCacheSize |
10000 |
記憶體中,索引的頁大小 |
enableIndexWriteAsync |
false |
索引是否非同步寫到訊息檔案中 |
journalMaxFileLength |
32mb |
一個訊息檔案的大小 |
enableJournalDiskSyncs |
true |
是否講非事務的訊息同步寫入到磁碟 |
cleanupInterval |
30000 |
清除操作週期,單位ms |
checkpointInterval |
5000 |
索引寫入到訊息檔案的週期,單位ms |
ignoreMissingJournalfiles |
false |
忽略丟失的訊息檔案,false,當丟失了訊息檔案,啟動異常 |
checkForCorruptJournalFiles |
false |
檢查訊息檔案是否損壞,true,檢查發現損壞會嘗試修復 |
checksumJournalFiles |
false |
產生一個checksum,以便能夠檢測journal檔案是否損壞。 |
5.4版本之後有效的屬性: |
|
|
archiveDataLogs |
false |
當為true時,歸檔的訊息檔案被移到directoryArchive,而不是直接刪除 |
directoryArchive |
null |
儲存被歸檔的訊息檔案目錄 |
databaseLockedWaitDelay |
10000 |
在使用負載時,等待獲得檔案鎖的延遲時間,單位ms |
maxAsyncJobs |
10000 |
同個生產者產生等待寫入的非同步訊息最大量 |
concurrentStoreAndDispatchTopics |
false |
當寫入訊息的時候,是否轉發主題訊息 |
concurrentStoreAndDispatchQueues |
true |
當寫入訊息的時候,是否轉發佇列訊息 |
5.6版本之後有效的屬性: |
|
|
archiveCorruptedIndex |
false |
是否歸檔錯誤的索引 |
每個KahaDB的例項都可以配置單獨的介面卡,如果沒有目標佇列提交給filteredKahaDB,那麼意味著對所有的佇列有效。如果一個佇列沒有對應的介面卡,那麼將會丟擲一個異常。配置如下:
- <persistenceAdapter>
- <mKahaDBdirectorymKahaDBdirectory="${activemq.base}/data/kahadb">
- <filteredPersistenceAdapters>
- <!-- match all queues -->
- <filteredKahaDBqueuefilteredKahaDBqueue=">">
- <persistenceAdapter>
- <kahaDBjournalMaxFileLengthkahaDBjournalMaxFileLength="32mb"/>
- </persistenceAdapter>
- </filteredKahaDB>
- <!-- match all destinations -->
- <filteredKahaDB>
- <persistenceAdapter>
- <kahaDBenableJournalDiskSyncskahaDBenableJournalDiskSyncs="false"/>
- </persistenceAdapter>
- </filteredKahaDB>
- </filteredPersistenceAdapters>
- </mKahaDB>
- </persistenceAdapter>
如果filteredKahaDB的perDestination屬性設定為true,那麼匹配的目標佇列將會得到自己對應的KahaDB例項。配置如下:
- <persistenceAdapter>
- <mKahaDBdirectorymKahaDBdirectory="${activemq.base}/data/kahadb">
- <filteredPersistenceAdapters>
- <!-- kahaDB per destinations -->
- <filteredKahaDB perDestination="true">
- <persistenceAdapter>
- <kahaDBjournalMaxFileLengthkahaDBjournalMaxFileLength="32mb" />
- </persistenceAdapter>
- </filteredKahaDB>
- </filteredPersistenceAdapters>
- </mKahaDB>
- </persistenceAdapter>
3、JDBC
可以將訊息儲存到資料庫中,例如:Mysql、SQL Server、Oracle、DB2。
配置JDBC介面卡:
- <persistenceAdapter>
- <jdbcPersistenceAdapterdataSourcejdbcPersistenceAdapterdataSource="#mysql-ds" createTablesOnStartup="false" />
- </persistenceAdapter>
dataSource指定持久化資料庫的bean,createTablesOnStartup是否在啟動的時候建立資料表,預設值是true,這樣每次啟動都會去建立資料表了,一般是第一次啟動的時候設定為true,之後改成false。
- Mysql持久化bean:
- <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/>
- <property name="username" value="activemq"/>
- <property name="password" value="activemq"/>
- <property name="poolPreparedStatements" value="true"/>
- </bean>
- SQL Server持久化bean:
- <bean id="mssql-ds" class="net.sourceforge.jtds.jdbcx.JtdsDataSource" destroy-method="close">
- <property name="serverName" value="SERVERNAME"/>
- <property name="portNumber" value="PORTNUMBER"/>
- <property name="databaseName" value="DATABASENAME"/>
- <property name="user" value="USER"/>
- <property name="password" value="PASSWORD"/>
- </bean>
- Oracle持久化bean:
- <bean id="oracle-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
- <property name="url" value="jdbc:oracle:thin:@10.53.132.47:1521:activemq"/>
- <property name="username" value="activemq"/>
- <property name="password" value="activemq"/>
- <property name="maxActive" value="200"/>
- <property name="poolPreparedStatements" value="true"/>
- </bean>
- DB2持久化bean:
- <bean id="db2-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver"/>
- <property name="url" value="jdbc:db2://hndb02.bf.ctc.com:50002/activemq"/>
- <property name="username" value="activemq"/>
- <property name="password" value="activemq"/>
- <property name="maxActive" value="200"/>
- <property name="poolPreparedStatements" value="true"/>
- </bean>
4、LevelDB
這種檔案系統是從ActiveMQ5.8之後引進的,它和KahaDB非常相似,也是基於檔案的本地資料庫儲存形式,但是它提供比KahaDB更快的永續性。與KahaDB不同的是,它不是使用傳統的B-樹來實現對日誌資料的提前寫,而是使用基於索引的LevelDB。
預設配置如下:
- <persistenceAdapter>
- <levelDBdirectorylevelDBdirectory="activemq-data"/>
- </persistenceAdapter>
屬性如下:
屬性名稱 |
預設值 |
描述 |
directory |
"LevelDB" |
資料檔案的儲存目錄 |
readThreads |
10 |
系統允許的併發讀執行緒數量 |
sync |
true |
同步寫到磁碟 |
logSize |
104857600 (100 MB) |
日誌檔案大小的最大值 |
logWriteBufferSize |
4194304 (4 MB) |
日誌資料寫入檔案系統的最大快取值 |
verifyChecksums |
false |
是否對從檔案系統中讀取的資料進行校驗 |
paranoidChecks |
false |
儘快對系統內部發生的儲存錯誤進行標記 |
indexFactory |
org.fusesource.leveldbjni.JniDBFactory, org.iq80.leveldb.impl.Iq80DBFactory |
在建立LevelDB索引時使用 |
indexMaxOpenFiles |
1000 |
可供索引使用的開啟檔案的數量 |
indexBlockRestartInterval |
16 |
Number keys between restart points for delta encoding of keys. |
indexWriteBufferSize |
6291456 (6 MB) |
記憶體中索引資料的最大值 |
indexBlockSize |
4096 (4 K) |
每個資料塊的索引資料大小 |
indexCacheSize |
268435456 (256 MB) |
使用快取索引塊允許的最大記憶體 |
indexCompression |
snappy |
適用於索引塊的壓縮型別 |
logCompression |
none |
適用於日誌記錄的壓縮型別 |
5、 下面詳細介紹一下如何將訊息持久化到Mysql資料庫中
Ø 需要將mysql的驅動包放置到ActiveMQ的lib目錄下
Ø 修改activeMQ的配置檔案:
- <persistenceAdapter>
- <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#mysql-ds"createTablesOnStartup="false"/>
- </persistenceAdapter>
在配置檔案中的broker節點外增加:
- <beanidbeanid="mysql-ds"class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <propertynamepropertyname="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url"value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true"/>
- <property name="username"value="root"/>
- <property name="password" value="root"/>
- <property name="maxActive"value="200"/>
- <propertynamepropertyname="poolPreparedStatements" value="true"/>
- </bean>
從配置中可以看出資料庫的名稱是activemq,需要手動在MySql中建立這個資料庫。
然後重新啟動activeMQ,會發現activemq多了三張表:
1:activemq_acks
2:activemq_lock
3:activemq_msgs
Ø 點到點型別
Sender類:
- 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 Sender {
- private static final int SEND_NUMBER = 2000;
- public static void main(String[] args) {
- // ConnectionFactory :連線工廠,JMS用它建立連線
- ConnectionFactory connectionFactory;
- // Connection :JMS客戶端到JMS Provider的連線
- Connection connection = null;
- // Session:一個傳送或接收訊息的執行緒
- Session session;
- // Destination :訊息的目的地;訊息傳送給誰.
- Destination destination;
- // MessageProducer:訊息傳送者
- MessageProducer producer;
- // TextMessage message;
- // 構造ConnectionFactory例項物件,此處採用ActiveMq的實現
- connectionFactory = new ActiveMQConnectionFactory(
- ActiveMQConnection.DEFAULT_USER,
- ActiveMQConnection.DEFAULT_PASSWORD,
- "tcp://localhost:61616");
- try{
- // 構造從工廠得到連線物件
- connection = connectionFactory.createConnection();
- //啟動
- connection.start();
- //獲取操作連線
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- //獲取session,FirstQueue是一個伺服器的queue destination = session.createQueue("FirstQueue");
- // 得到訊息生成者【傳送者】
- producer = session.createProducer(destination);
- //設定不持久化
- producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
- //構造訊息
- sendMessage(session, producer);
- //session.commit();
- connection.close();
- }
- catch(Exception e){
- e.printStackTrace();
- }finally{
- if(null != connection){
- try {
- connection.close();
- } catch (JMSException e) {
- // TODO Auto-generatedcatch block
- e.printStackTrace();
- }
- }
- }
- }
- public static void sendMessage(Session session, MessageProducer producer)throws Exception{
- for(int i=1; i<=SEND_NUMBER; i++){
- TextMessage message = session.createTextMessage("ActiveMQ傳送訊息"+i);
- System.out.println("傳送訊息:ActiveMQ傳送的訊息"+i);
- producer.send(message);
- }
- }
- }
Receiver類:
- import javax.jms.Connection;
- import javax.jms.ConnectionFactory;
- import javax.jms.Destination;
- import javax.jms.MessageConsumer;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import org.apache.activemq.ActiveMQConnection;
- import org.apache.activemq.ActiveMQConnectionFactory;
- public class Receiver {
- public static void main(String[] args) {
- // ConnectionFactory :連線工廠,JMS用它建立連線
- ConnectionFactory connectionFactory;
- // Connection :JMS客戶端到JMS Provider的連線
- Connection connection = null;
- // Session:一個傳送或接收訊息的執行緒
- Session session;
- // Destination :訊息的目的地;訊息傳送給誰.
- Destination destination;
- // 消費者,訊息接收者
- MessageConsumer consumer;
- connectionFactory = newActiveMQConnectionFactory(
- ActiveMQConnection.DEFAULT_USER,
- ActiveMQConnection.DEFAULT_PASSWORD,
- "tcp://localhost:61616");
- try {
- //得到連線物件
- connection =connectionFactory.createConnection();
- // 啟動
- connection.start();
- // 獲取操作連線
- session = connection.createSession(false,
- Session.AUTO_ACKNOWLEDGE);
- // 建立Queue
- destination = session.createQueue("FirstQueue");
- consumer =session.createConsumer(destination);
- while(true){
- //設定接收者接收訊息的時間,為了便於測試,這裡定為100s
- TextMessagemessage = (TextMessage)consumer.receive(100000);
- if(null != message){
- System.out.println("收到訊息" +message.getText());
- }else break;
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally {
- try {
- if (null != connection)
- connection.close();
- } catch (Throwable ignore) {
- }
- }
- }
- }
測試:
測試一:
A、 先執行Sender類,待執行完畢後,執行Receiver類
B、 在此過程中activemq資料庫的activemq_msgs表中沒有資料
C、 再次執行Receiver,消費不到任何資訊
測試二:
A、 先執行Sender類
B、 重啟電腦
C、 執行Receiver類,無任何資訊被消費
測試三:
A、 把Sender類中的producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);改為producer.setDeliveryMode(DeliveryMode.PERSISTENT);
B、 先執行Sender類,待執行完畢後,執行Receiver類
C、 在此過程中activemq資料庫的activemq_msgs表中有資料生成,執行完Receiver類後,資料清除
測試四:
A、 把Sender類中的producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);改為producer.setDeliveryMode(DeliveryMode.PERSISTENT);
B、 執行Sender類
C、 重啟電腦
D、 執行Receiver類,有訊息被消費
結論:
通過以上測試,可以發現,在P2P型別中當DeliveryMode設定為NON_PERSISTENCE時,訊息被儲存在記憶體中,而當DeliveryMode設定為PERSISTENCE時,訊息儲存在broker的相應的檔案或者資料庫中。而且P2P中訊息一旦被Consumer消費就從broker中刪除。
Ø 釋出/訂閱型別
Sender類:
- 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 javax.jms.Topic;
- import org.apache.activemq.ActiveMQConnection;
- import org.apache.activemq.ActiveMQConnectionFactory;
- public class Sender {
- private static final int SEND_NUMBER = 100;
- public static void main(String[] args) {
- // ConnectionFactory :連線工廠,JMS用它建立連線
- ConnectionFactory connectionFactory;
- // Connection :JMS客戶端到JMS Provider的連線
- Connection connection = null;
- // Session:一個傳送或接收訊息的執行緒
- Session session;
- // MessageProducer:訊息傳送者
- MessageProducer producer;
- // TextMessage message;
- // 構造ConnectionFactory例項物件,此處採用ActiveMq的實現
- connectionFactory = new ActiveMQConnectionFactory(
- ActiveMQConnection.DEFAULT_USER,
- ActiveMQConnection.DEFAULT_PASSWORD,
- "tcp://localhost:61616");
- try{
- //得到連線物件
- connection = connectionFactory.createConnection();
- //啟動
- connection.start();
- //獲取操作連線
- session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- Topic topic = session.createTopic("MQ_test");
- // 得到訊息生成者【傳送者】
- producer = session.createProducer(topic);
- //設定持久化
- producer.setDeliveryMode(DeliveryMode.PERSISTENT);
- //構造訊息
- sendMessage(session, producer);
- //session.commit();
- connection.close();
- }
- catch(Exception e){
- e.printStackTrace();
- }finally{
- if(null != connection){
- try {
- connection.close();
- } catch (JMSException e) {
- // TODO Auto-generatedcatch block
- e.printStackTrace();
- }
- }
- }
- }
- public static void sendMessage(Session session, MessageProducer producer)throws Exception{
- for(int i=1; i<=SEND_NUMBER; i++){
- TextMessage message = session.createTextMessage("ActiveMQ傳送訊息"+i);
- System.out.println("傳送訊息:ActiveMQ傳送的訊息"+i);
- producer.send(message);
- }
- }
- }
Receiver類:
- import javax.jms.Connection;
- import javax.jms.ConnectionFactory;
- import javax.jms.Destination;
- import javax.jms.MessageConsumer;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import javax.jms.Topic;
- import org.apache.activemq.ActiveMQConnection;
- import org.apache.activemq.ActiveMQConnectionFactory;
- public class Receiver {
- public static void main(String[] args) {
- // ConnectionFactory :連線工廠,JMS用它建立連線
- ConnectionFactory connectionFactory;
- // Connection :JMS客戶端到JMS Provider的連線
- Connection connection = null;
- // Session:一個傳送或接收訊息的執行緒
- Session session;
- // 消費者,訊息接收者
- MessageConsumer consumer;
- connectionFactory = newActiveMQConnectionFactory(
- ActiveMQConnection.DEFAULT_USER,
- ActiveMQConnection.DEFAULT_PASSWORD,
- "tcp://localhost:61616");
- try {
- // 構造從工廠得到連線物件
- connection =connectionFactory.createConnection();
- connection.setClientID("clientID001");
- // 啟動
- connection.start();
- // 獲取操作連線
- session = connection.createSession(false,
- Session.AUTO_ACKNOWLEDGE);
- // 獲取session
- Topic topic = session.createTopic("MQ_test");
- // 得到訊息生成者【傳送者】
- consumer = session.createDurableSubscriber(topic, "MQ_sub");
- while(true){
- //設定接收者接收訊息的時間,為了便於測試,這裡誰定為100s
- TextMessagemessage = (TextMessage)consumer.receive(100000);
- if(null != message){
- System.out.println("收到訊息" +message.getText());
- }else break;
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally {
- try {
- if (null != connection)
- connection.close();
- } catch (Throwable ignore) {
- }
- }
- }
- }
測試:
測試一:
A、先啟動Sender類
B、再啟動Receiver類
C、結果無任何記錄被訂閱
測試二:
A、先啟動Receiver類,讓Receiver在相關主題上進行訂閱
B、停止Receiver類,再啟動Sender類
C、待Sender類執行完成後,再啟動Receiver類
D、結果發現相應主題的資訊被訂閱
原文地址: http://blog.csdn.net/xyw_blog/article/details/9128219
相關文章
- ActiveMQ-mysql持久化MQMySql持久化
- Redis的持久化方式Redis持久化
- redis學習手記(二)持久化方式Redis持久化
- Redis資料持久化—RDB持久化與AOF持久化Redis持久化
- 一起看懂Redis兩種持久化方式的原理Redis持久化
- redis系列:RDB持久化與AOF持久化Redis持久化
- Redis持久化的兩種方式的優缺點介紹Redis持久化
- [Redis]持久化Redis持久化
- Docker 持久化Docker持久化
- redis持久化Redis持久化
- Redis - 持久化Redis持久化
- Redis 持久化Redis持久化
- redisaof持久化Redis持久化
- 在K8S中,資料持久化的方式有哪些?K8S持久化
- springboot+quartz以持久化的方式實現定時任務Spring Bootquartz持久化
- SpringCloud使用Sentinel,Sentinel持久化,Sentinel使用nacos持久化SpringGCCloud持久化
- 一文帶你深入瞭解 Redis 的持久化方式及其原理Redis持久化
- 雲原生時代 容器持久化儲存的最佳方式是什麼?持久化
- redis 持久化策略Redis持久化
- Redis的持久化Redis持久化
- Redis 的持久化Redis持久化
- redis 之 持久化Redis持久化
- 可持久化trie持久化
- Redis 七 持久化Redis持久化
- Redis 持久化方案Redis持久化
- redis ——AOF持久化Redis持久化
- Redis 持久化(Persistence)Redis持久化
- Redis:持久化篇Redis持久化
- redis-持久化Redis持久化
- ActiveMQMQ
- RDD持久化,不使用RDD持久化的問題的工作原理持久化
- Docker資料持久化Docker持久化
- redis持久化機制Redis持久化
- 可持久化 01 trie持久化
- Java emoji持久化mysqlJava持久化MySql
- Redis的持久化方案Redis持久化
- 使用 Java 持久化 APIJava持久化API
- Redis的持久化策略Redis持久化
- 可持久化專題持久化