我們在使用原生ActiveMQ的API編程中,介紹ActiveMQ的使用過程中,在介紹其Point-to-Point(P2P) /點對點模式時,我們發現在該模式下消息時不會丟失的
那麼這裏是如果做到消息的持久化呢?ActiveMQ提供了幾種持久化方式:
- AMQ消息存儲,基於文件的存儲方式,它具有寫入速度快和容易恢復的特點。消息存儲在一個個文件中,文件的默認大小爲32M,如果一條消息的大小超過了32M,那麼這個值必須設置大一點。當一個存儲文件中的消息已經全部被消費,那麼這個文件將被標識爲可刪除,在下一個清除階段,這個文件被刪除。AMQ適用於ActiveMQ5.3之前的版本。
- KahaDB消息存儲,提供了容量的提升和恢復能力,是現在的默認存儲方式;KahaDB是基於文件的本地數據庫儲存形式,雖然沒有AMQ的速度快,但是它具有強擴展性,恢復的時間比AMQ短,從5.4版本之後KahaDB做爲默認的持久化方式。
- JDBC消息存儲,消息基於JDBC存儲的。(JDBC Message Store with ActiveMQ Journal優化了JDBC Store消息存儲)
- Memory消息存儲,基於內存的消息存儲,就是消息存儲在內存中。這裏沒有動態的緩存存在,所以你必須注意設置你的broker所在的JVM和內存限制。這種方式的持久化消息只在當前JVM內有效,當重啓JVM之後會丟失持久化的消息。
這裏我們就來看一看ActiveMQ的conf路徑下的activemq配置文件,如下:
從上述可以看出,ActiveMQ默認的持久化機制爲KahaDB消息存儲,所以即使你不配置任何的KahaDB參數信息,ActiveMQ也會啓動KahaDB。這種情況下,KahaDB文件所在位置是你的ActiveMQ安裝路徑下的/data/kahadb
子目錄,如下:
- db.data 它是消息的索引文件,本質上是B-Tree(B樹),使用B-Tree作爲索引指向
db-*.log
裏面存儲的消息 - db.redo 用來進行消息恢復
- db-*.log 存儲消息內容。新的數據以APPEND的方式追加到日誌文件末尾。屬於順序寫入,因此消息存儲是比較 快的。默認是32M,達到閥值會自動遞增
- lock 文件鎖,寫入當前獲得kahadb讀寫權限的broker ,用於在集羣環境下的競爭處理
系統中默認的kahadb的配置,只爲我們配置的directory
來指定文件的路徑,其實我們還可以配置更多的屬性,如下:
- director: KahaDB存放的路徑,默認值activemq-data
- indexWriteBatchSize: 批量寫入磁盤的索引page數量,默認值爲1000
- indexCacheSize: 內存中緩存索引page的數量,默認值10000
- enableIndexWriteAsync: 是否異步寫出索引,默認false
- journalMaxFileLength: 設置每個消息data log的大小,默認是32MB
- enableJournalDiskSyncs: 設置是否保證每個沒有事務的內容,被同步寫入磁盤,JMS持久化的時候需要,默認爲true
- cleanupInterval: 在檢查到不再使用的消息後,在具體刪除消息前的時間,默認30000
- checkpointInterval: checkpoint的間隔時間,默認是5000
- ignoreMissingJournalfiles: 是否忽略丟失的消息日誌文件,默認false
- checkForCorruptJournalFiles: 在啓動的時候,將會驗證消息文件是否損壞,默認false
- checksumJournalFiles: 是否爲每個消息日誌文件提供checksum,默認false
- archiveDataLogs: 是否移動文件到特定的路徑,而不是刪除它們,默認false
- directoryArchive: 定義消息已經被消費過後,移動data log到的路徑,默認null
- databaseLockedWaitDelay: 獲得數據庫鎖的等待時間(used by shared master/slave),默認10000
- maxAsyncJobs: 設置最大的可以存儲的異步消息隊列,默認值10000,可以和concurrent MessageProducers設置成一樣的值。
- concurrentStoreAndDispatchTransactions: 是否分發消息到客戶端,同時事務存儲消息,默認true
- concurrentStoreAndDispatchTopics: 是否分發Topic消息到客戶端,同時進行存儲,默認true
- concurrentStoreAndDispatchQueues: 是否分發queue消息到客戶端,同時進行存儲,默認true
從ActiveMQ 4+版本開始,ActiveMQ就支持使用關係型數據庫進行持久化存儲——通過JDBC實現的數據庫連接。可以使用的關係型數據庫囊括了目前市面的主流數據庫。
接下來我們就來將默認的KahaDB的存儲機制改爲JDBC的形式,首先我們在conf下的activemq.xml
中,將默認配置的kahbdb改成jdbc,如下
然後我們就需要配置上述指定的名爲dataSource的數據庫連接,其實爲Spring的配置是類似的,如下:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/activemq?relaxAutoCommit=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
注:其中url屬性值中配置的&
即&
的轉義字符,其中第一個屬性relaxAutoCommit=true
是必需的。
然後我們在本地數據中建立一個相同的數據庫名activemq
即口,然後就可以啓動我們的ActiveMQ服務了,在啓動ActiveMQ之前,我們需要在ActiveMQ安裝目錄下的lib目錄下添加相關依賴jar包,就是用於上述創建和數據庫的連接使用
這裏版本沒有太大要求,但是最好保持一致,比如這裏我們引入dbcp2的包,那麼配置文件中肯定也要是dbcp2.BasicDataSource
添加完依賴jar包後,就可以啓動ActiveMQ了,然後我們就會發現在activemq數據庫中多出了三種表,如下:
- ACTIVEMAQ_ACKS,用於存儲訂閱關係。如果是持久化Topic,訂閱者和服務器的訂閱關係在這個表保存
- CONTAINER: 消息的目的地Destination
- SUB_DEST: 如果是使用static集羣,這個字段會有集羣其他系統的信息
- CLIENT_ID: 每個訂閱者都必須有一個唯一的客戶端id用以區分
- SUB_NAME: 訂閱者名稱
- SELECTOR: 選擇器,可以選擇只消費滿足條件的消息。條件可以用自定義屬性實現,可支持多屬性and和or操作
- LAST_ACKED_ID: 記錄消費過的消息的ID
- ACTIVEMQ_LOCK,在集羣環境中才有用,只有一個Broker可以獲得消息,稱爲Master Broker,其他的只能作爲備份等待Master Broker不可用,纔可能成爲下一個Master Broker。這個表用於記錄哪個Broker是當前的Master Broker。
- ACTIVEMQ_MSGS,用於存儲消息,Queue和Topic都存儲在這個表中
- ID: 自增的數據庫主鍵
- CONTAINER: 消息的目的地Destination
- MSGID_PROD: 消息發送者在客戶端的主鍵
- MSGID_SEQ: 是發送消息的順序,MSGID_PROD + MSGID_SEQ可以組成JMS的MessageID
- EXPIRATION: 消息的過期時間,存儲的是從1970-01-01到現在的毫秒數
- MSG: 消息本體的java序列化對象的二進制數據
- PRIORITY: 優先級,從0-9,數值越大優先級越高
這裏我們在Point-to-Point(P2P) /點對點模式下,來啓動一個消息生成者,但是不啓動消費者,然後來看看ACTIVEMQ_MSGS表的信息,如下:
這裏我們就可以看到消息生成者發送的三條數據,消費者還未進行消費,就可以在ACTIVEMQ_MSGS查看到信息,然後我們在啓動消息的消費者,該表中的三條數據就會被消費掉了(即表中數據被刪除)
JDBC Message Store with ActiveMQ Journal可以看做優化版的JDBC存儲,這種方式克服了JDBC Store的不足,JDBC存儲每次消息過來,都需要去寫庫和讀庫。而JDBC Message Store with ActiveMQ Journal使用延遲存儲數據到數據庫,當消息來到時先緩存到文件中,延遲後才寫到數據庫中。
當消費者的消費速度能夠及時跟上生產者消息的生產速度時,journal文件能夠大大減少需要寫入到DB中的消息。 舉個例子,生產者生產了1000條消息,這1000條消息會保存到journal文件,如果消費者的消費速度很快的情況 下,在journal文件還沒有同步到DB之前,消費者已經消費了90%的以上的消息,那麼這個時候只需要同步剩餘的 10%的消息到DB。 如果消費者的消費速度很慢,這個時候journal文件可以使消息以批量方式寫到DB。
其配置如下:
<persistenceFactory>
<journalPersistenceAdapterFactory journalLogFiles="4" journalLogFileSize="32768" useJournal="true" useQuickJournal="true" dataSource="#dataSource" dataDirectory="activemq-data"/>
</persistenceFactory>
Memory內存消息存儲主要是存儲所有的持久化的消息在內存中。這裏沒有動態的緩存存在,所以你必須注意設置你的broker所在的JVM和內存限制。
這種方式的持久化消息只在當前JVM內有效,當重啓JVM之後會丟失持久化的消息。
配置方式如下:只需要將persistent
屬性設爲false即可
還有一種就是AMQ消息存儲,它是一個基於文件、事務存儲設計爲快速消息存儲的一個結構,該結構是以流的形式來進行消息交互的。
其配置如下: