ActiveMQ的持久消息存儲方案

ActiveMQ的持久消息存儲方案

        當ActiveMQ接收到PERSISTENT Message消息後就需要藉助持久化方案來完成PERSISTENT Message的存儲。這個介質可以是磁盤文件系統、可以是ActiveMQ的內置數據庫,還可以是某種外部提供的關係型數據庫。本節筆者將向讀者講解三種ActiveMQ推薦的存儲方案的配置使用。

這裏寫圖片描述

  • 如上圖2.1的步驟所示,所有PERSISTENT Message都要執行持久化存儲操作,持久化存儲操作方案的性能直接影響着整個MQ服務端的PERSISTENT Message吞吐性能。另外NON_PERSISTENT Message雖然不會進行持久化存儲,但是NON_PERSISTENT Message也不是永遠都只存在與內存區域。

  • 有的讀者會問,Topic模式的工作隊列在沒有任何活動訂閱者的情況下也會對PERSISTENT Message進行持久化存儲嗎?當然會,因爲Topic模式的工作隊列還要考慮“Durable Topic Subscribers”形式的訂閱者。即使沒有“Durable Topic Subscribers”形式的訂閱者,先存儲再標記的過程也不會改變(只是不一定真正進入物理磁盤)。

  • 有的讀者還會問,在客戶端啓動事務的情況下,如果沒有事務的commit操作,PERSISTENT Message也會進行持久化存儲嗎?當然還是會,前文我們已經講過沒有做事務的commit只是說這些事務中的消息不會進行確認操作,不會分發到某個指定的具體隊列中;但是只要使用了send方法,PERSISTENT Message就會被髮送到服務端,就會進行持久化存儲操作

  • 如上圖中被標示爲2.2的操作步驟所示,在ActiveMQ設置的持久化方案完成某條消息的持久化後,會在ActiveMQ服務節點的內部發出一個“完成”信號。這是爲了告訴ActiveMQ服務節點自己,是否可以進行下一步操作。但是爲了加快ActiveMQ服務節點內部的處理效率,這個過程可以設置爲“異步”。

  • 那麼進行了持久化存儲的PERSISTENT Message什麼時候被刪除呢?就如同之前我們提到的一樣,ActiveMQ服務端只有在收到消費者端某一條消息或某一組消息的ACK標示後,纔會認爲消息被消費者端正確處理了。就是在這個時候,ActiveMQ會通知持久化方案,進行刪除這一條或者這一組消息的操作,並空閒出相應的存儲空間。如上圖被標示爲5.1的操作步驟所示。

1、存儲方案配置

在介紹ActiveMQ的存儲方案之前,首先需要明確的是ActiveMQ中的幾種“容量”描述:

  • ActiveMQ的內核是Java編寫的,也就是說如果服務端沒有Java運行環境ActiveMQ是無法運行的。ActiveMQ啓動時,啓動腳本使用wrapper包裝器來啓動JVM。JVM相關的配置信息在啓動目錄的“wrapper.conf”配置文件中。各位讀者可以通過改變其中的配置項,設置JVM的初始內存大小和最大內存大小(當然還可以進行其他和JVM有關的設置,例如開啓debug模式),如下:
......

# Initial Java Heap Size (in MB)
wrapper.java.initmemory=100

# Maximum Java Heap Size (in MB)
wrapper.java.maxmemory=512

......

以上配置項設置JVM的初始內存大小爲100MB,設置JVM的最大內存大小爲512MB。如果您在更改後使用console參數啓動ActiveMQ,那麼會看到當前ActiveMQ的JVM設置發生了變化:

這裏寫圖片描述

  • 明確了ActiveMQ的內存區域來源,纔好理解後續的設置內容。ActiveMQ每一個服務節點都是一個獨立的進程。在ActiveMQ主配置文件中,讀者可以找到一個“systemUsage”標記,類似定義如下:
<systemUsage>
    <systemUsage>
        <memoryUsage>
            <memoryUsage percentOfJvmHeap="70" />
        </memoryUsage>
        <storeUsage>
            <storeUsage limit="100 gb"/>
        </storeUsage>
        <tempUsage>
            <tempUsage limit="50 gb"/>
        </tempUsage>
    </systemUsage>
</systemUsage>

systemUsage:該標記用於設置整個ActiveMQ節點在進程級別的各種“容量”的設置情況。其中可設置的屬性包括:sendFailIfNoSpaceAfterTimeout,當ActiveMQ收到一條消息時,如果ActiveMQ這時已經沒有多餘“容量”了,那麼就會等待一段時間(這裏設置的毫秒數),如果超過這個等待時間ActiveMQ仍然沒有可用的容量,那麼就拒絕接收這條消息並在消息的發送端拋出javax.jms.ResourceAllocationException異常;sendFailIfNoSpace,當ActiveMQ收到一條消息時,如果ActiveMQ這時已經沒有多餘“容量”了,就直接拒絕這條消息(不用等待一段時間),並在消息的發送端拋出javax.jms.ResourceAllocationException異常。

memoryUsage:該子標記設置整個ActiveMQ節點的“可用內存限制”。這個值不能超過上文中您設置的JVM maxmemory的值。其中的percentOfJvmHeap屬性表示使用“百分數值”進行設置,除了這個屬性以外,您還可以使用limit屬性進行固定容量授權,例如:limit=”1000 mb”。這些內存容量將供所有隊列使用。

storeUsage:該標記設置整個ActiveMQ節點,用於存儲“持久化消息”的“可用磁盤空間”。該子標記的limit屬性必須要進行設置。在使用後續介紹的KahaDB方案或者LevelDB方案進行PERSISTENT Message持久化存儲時,這個storeUsage屬性都會起作用;但是如果使用數據庫存儲方案,這個屬性就不會起作用了。

tempUsage:在ActiveMQ 5.X+ 版本中,一旦ActiveMQ服務節點存儲的消息達到了memoryUsage的限制,NON_PERSISTENT Message就會被轉儲到 temp store區域。雖然我們說過NON_PERSISTENT Message不進行持久化存儲,但是ActiveMQ爲了防止“數據洪峯”出現時NON_PERSISTENT Message大量堆積致使內存耗盡的情況出現,還是會將NON_PERSISTENT Message寫入到磁盤的臨時區域——temp store。這個子標記就是爲了設置這個temp store區域的“可用磁盤空間限制”。最後提醒各位讀者storeUsage和tempUsage並不是“最大可用空間”,而是一個閥值。

2、ActiveMQ中持久化存儲方案的演化

說到ActiveMQ中持久化存儲方案的演化問題,如果您仔細閱讀ActiveMQ官方文檔中關於持久化部分的描述,您就不難發現ActiveMQ的開發團隊在針對持久化性能問題的優化上可謂與時俱進。這也符合一款健壯軟件的生命週期特徵:任何功能特性都在進行不斷累計完善:

從最初的AMQ Message Store方案,到ActiveMQ V4版本中推出的High performance journal(高性能事務支持)附件 ,並且同步推出了關於關係型數據庫的存儲方案。ActiveMQ 5.3版本中又推出了對KahaDB的支持(V5.4版本後稱爲ActiveMQ默認的持久化方案),後來ActiveMQ V5.8版本開始支持LevelDB,到現在,V5.9+版本提供了標準的Zookeeper+LevelDB集羣化方案。下面我們重點介紹一下ActiveMQ中KahaDB、LevelDB和關係型數據庫這三種持久化存儲方案。並且會和讀者一起,使用Zookeeper搭建LevelDB集羣存儲方案。

對於最初的AMQ Message Store方案,ActiveMQ官方已不再推薦使用(實際上在筆者的實際工作中,也不會使用AMQ Message Store)。如果各位讀者想進行了解可以自行搜索相關資料,這裏不再進行介紹。

3、KahaDB存儲方案

3-1、KahaDB基本結構

KahaDB is a file based persistence database that is local to the message broker that is using it. It has been optimised for fast persistence and is the the default storage mechanism from ActiveMQ 5.4 onwards. KahaDB uses less file descriptors and provides faster recovery than its predecessor, the AMQ Message Store.

以上引用自Apache ActiveMQ 官方對KahaDB的定義。首先KahaDB基於文件系統,其次KahaDB支持事務。在ActiveMQ V5.4版本及後續版本KahaDB都是ActiveMQ的默認持久化存儲方案。最後Apache ActiveMQ官方表示它用來替換之前的AMQ Message Store存儲方案。

KahaDB主要元素包括:一個內存Metadata Cache用來在內存中檢索消息的存儲位置、若干用於記錄消息內容的Data log文件、一個在磁盤上檢索消息存儲位置的Metadata Store、還有一個用於在系統異常關閉後恢復Btree結構的redo文件。如下圖所示(官網引用):

這裏寫圖片描述

以下是KahaDB在磁盤文件上的現實展示。注意,可能您查看自己測試實例中所運行的KahaDB,看到的效果和本文中給出的效果不完全一致。例如您的data log文件可能叫db-1.log,也有可能會多出一個db.free的文件,但是這些都不影響我們對文件結構的分析:

[root@localhost KahaDB]# ll -h
總用量 29M
-rw-r--r--. 1 root root  32M 4   7 04:53 db-3.log
-rw-r--r--. 1 root root 7.6M 4   7 04:53 db.data
-rw-r--r--. 1 root root 2.8M 4   7 04:53 db.redo
-rw-r--r--. 1 root root    8 4   7 04:50 lock
  • db-3.log:這個文件就是我們上文提到的Data log文件。一個KahaDB中,可能同時存在多個Data log文件,他們存儲了每一條持久化消息的真正內容。這些Data log文件統一採用db-*.log的格式進行命名,並且每個Data log文件默認的大小都是32M(當然是可以進行設置的)。當一個Data log文件中的所有消息全部被成功消息後,這個Data log文件會在Metadata Cache中被標記爲刪除,並在下個checkpoint週期進行刪除操作。

  • 各位讀者可能已經注意到一個現象:爲什麼db-3.log的默認佔用大小就是32M,但是目錄顯示的“總用量”卻只有29M呢?在這個文件夾中,除了db-3.log文件本身,加上其他幾個文件所佔用的大小,已經遠遠超過了32M!這是因爲,爲了加快寫文件的性能,Data log文件採用順序寫的方式進行操作,爲了保證文件使用的扇區在物理上是連續的,所以Data log文件需要預佔這些扇區(這個和Hadoop中每一個block大小都是固定的原因相似)。雖然您看到Data log文件佔用的32M的磁盤空間,但是這些磁盤空間並沒有全部使用。另外,關於隨機讀寫和連續讀寫的巨大性能差異,我會在今年下半年新的“數據存儲專欄”中,進行詳細介紹。

  • 爲了更快的找到某個具體消息在Data log文件中的具體位置。消息的位置索引採用BTree的結構被存儲在內存中,這個內存區域就是上文提到的Metadata Cache(大小也是可以設置的)。要知道Mysql的Innodb 存儲引擎也是採用BTree結構構造索引結構(用了都說快哦~~)。所以一般情況下,只要某個隊列有活動的消費者存在,消息的定位、讀取操作是可以很快完成的。

  • 內存中沒有被處理的消息索引會以一定的週期(或者一定的數量規模)爲依據,同步(checkpoint)到Metadata Store中,這就是我們在上文中看到的“db.data”文件。當然db.redo文件也會被更新,以便在ActiveMQ服務節點在重啓後對Metadata Cache進行恢復。最後,消息同步(checkpoint)依據,可以在ActiveMQ的主配置文件中進行設置。

3-2、在ActiveMQ中配置KahaDB

由於在ActiveMQ V5.4+的版本中,KahaDB是默認的持久化存儲方案。所以即使您不配置任何的KahaDB參數信息,ActiveMQ也會啓動KahaDB。這種情況下,KahaDB文件所在位置是您的ActiveMQ安裝路徑下的/data/${broker.Name}/KahaDB子目錄。其中${broker.Name}代表這個ActiveMQ服務節點的名稱。

正式的生產環境還是建議您在主配置文件中明確設置KahaDB的工作參數。如下所示:

......
<broker xmlns="http://activemq.apache.org/schema/core"  brokerName="localhost" dataDirectory="${activemq.data}">
    ......
    <persistenceAdapter>
        <kahaDB directory="${activemq.data}/kahadb"/>
    </persistenceAdapter>
    ......
</broker>
......

以上配置項設置使用kahaDB爲持久化存儲方法,並且設置kahaDB的工作目錄爲ActiveMQ安裝路勁下/data/kahadb目錄。如果您需要Data log文件默認的32M的大小,可以使用journalMaxFileLength屬性進行設置,如下所示:

......
<broker xmlns="http://activemq.apache.org/schema/core"  brokerName="localhost" dataDirectory="${activemq.data}">
    ......
    <persistenceAdapter>
        <kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="64mb"/>
    </persistenceAdapter>
    ......
</broker>
......

您還可以設置爲:當Metadata Cache中和Metadata Store中不同的索引條數達到500條時,就進行checkpoint同步。如下所示:

......
<broker xmlns="http://activemq.apache.org/schema/core"  brokerName="localhost" dataDirectory="${activemq.data}">
    ......
    <persistenceAdapter>
        <kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="64mb" indexWriteBatchSize="500"/>
    </persistenceAdapter>
    ......
</broker>
......

以下表格爲讀者示例了KahaDB中所有的配置選項和其含義(引用自網絡,加“*”部分是筆者認爲重要的配置選項):

property namedefault valueComments
*directoryactivemq-data消息文件和日誌的存儲目錄
*indexWriteBatchSize1000當Metadata cache區域和Metadata store區域不同的索引數量達到這個值後,Metadata cache將會發起checkpoint同步
*indexCacheSize10000內存中,索引的頁大小。超過這個大小Metadata cache將會發起checkpoint同步
*enableIndexWriteAsyncfalse索引是否異步寫到消息文件中,將以不要設置爲true
*journalMaxFileLength32mb一個消息文件的大小
*enableJournalDiskSyncstrue如果爲true,保證使用同步寫入的方式持久化消息到journal文件中
*cleanupInterval30000清除(清除或歸檔)不再使用的db-*.log文件的時間週期(毫秒)。
*checkpointInterval5000寫入索引信息到metadata store中的時間週期(毫秒)
ignoreMissingJournalfilesfalse是否忽略丟失的journal文件。如果爲false,當丟失了journal文件時,broker啓動時會拋異常並關閉
checkForCorruptJournalFilesfalse檢查消息文件是否損壞,true,檢查發現損壞會嘗試修復
checksumJournalFilesfalse產生一個checksum,以便能夠檢測journal文件是否損壞。
  • 5.4版本之後有效的屬性:
property namedefault valueComments
*archiveDataLogsfalse當爲true時,歸檔的消息文件被移到directoryArchive,而不是直接刪除
*directoryArchivenull存儲被歸檔的消息文件目錄
databaseLockedWaitDelay10000在使用負載時,等待獲得文件鎖的延遲時間,單位ms
maxAsyncJobs10000等待寫入journal文件的任務隊列的最大數量。應該大於或等於最大併發producer的數量。配合並行存儲轉發屬性使用。
concurrentStoreAndDispatchTopicsfalse如果爲true,轉發消息的時候同時提交事務
concurrentStoreAndDispatchQueuestrue如果爲true,轉發Topic消息的時候同時存儲消息的message store中
  • 5.6版本之後有效的屬性:
property namedefault valueComments
archiveCorruptedIndexfalse是否歸檔錯誤的索引到Archive文件夾下
  • 5.10版本之後有效的屬性:
property namedefault valueComments
IndexDirectory 單獨設置KahaDB中,db.data文件的存儲位置。如果不進行設置,db.data文件的存儲位置還是將以directory屬性設置的值爲準

4、LevelDB存儲方案

LevelDb是能夠處理十億級別規模Key-Value型數據持久性存儲的C++ 程序庫,由Google發起並開源。LevelDB只能由本操作系統的其他進程調用,所以它不具有網絡性。如果您需要網絡上的遠程進程操作LevelDB,那麼就要自行封裝服務層。

4-1、LevelDB基本結構

LevelDB中的核心設計算法是跳躍表(Skip List),核心操作策略是對磁盤上的數據日誌結構進行歸併(LSM)。跳躍表實際上是二叉平衡樹的一種變形結構,它通過將一個有序鏈表進行“升維”操作,從而減少每一層上需要遍歷的數據數量,達到快速查找的目的。下圖示意了一個跳躍表結構(在實際工作中,跳躍表的層級和“升維”策略的不同,跳躍表的結構也不一樣):

這裏寫圖片描述

您可以將上圖中的每個元素節點,想象成每一條消息的key值。爲了講解方便,上圖中我將擁有全部數據的元素的跳躍層稱爲Level 2(最高層),但實際上規範的跳躍表結構中,擁有全部元素的層次稱爲Level 0(最底層)。跳躍表的結構並非一成不變,當有一條新的記錄需要插入到結構時,可能會引起表中的多個Level都發生變化。

那麼LevelDB是如何應用跳躍表結構的?又是如何進行歸併操的?我們首先來看看LevelDB的簡要結構:

這裏寫圖片描述

  • Log文件

當LevelDB收到新的消息是會同步寫兩個地方:內存中的MemTable區域和磁盤上的Log文件。直接寫Log文件是爲了在系統異常退出並重啓時,能夠將LevelDB恢復到退出前的結構;那麼有的讀者會問,由於是直接寫磁盤會不會成爲性能瓶頸呢?答案是,LevelDB的log文件操作採用預佔磁盤空間(默認爲100MB),進行順序寫的方式。並且這個過程可以設置爲異步的(當然如果設置成異步的,可能需要接受異常情況下數據丟失的風險)。

  • MemTable和Immutable

LevelDB還寫將消息寫入內存的MemTable區域,MemTable區域的的數據組織結構就是跳躍表(Skip List),這樣的數據組織結構可以在讀取內存中信息的時候,快速完成信息定位。當MemTable區域的數據量達到indexWriteBufferSize屬性設置的大小時(默認爲6MB),LevelDB就會把這個MemTable區域標記爲Immutable,並開啓一個新的MemTable區域。一定注意,是標記爲Immutable,而不是把MemTable區域的數據拷貝到某一個Immutable區域。

新標記的Immutable區域中的數據會被執行Compact操作,從而寫入到磁盤上的.sst文件中。所謂Compact操作是指:LevelDB會剔除Immutable區域中那些已經被標示爲“刪除”的數據(成功消費的數據就會被標記爲“刪除”),排除那些格式錯誤的數據,並可能進行數據壓縮。

  • SSTable文件

SSTable文件是指存在於硬盤上,後綴名爲.sst的文件。這些文件是LevelDB磁盤上最重要的數據記錄文件,每一個SSTable文件的默認大小爲2MB,也就是說LevelDB的文件夾下會有很多的.sst文件。SSTable文件並不是順序寫的,而是按照數據的key排序進行隨機寫,所以SSTable文件無需預佔存儲磁盤存儲空間

借鑑於跳躍表的設計思想,SSTable文件也是分層次的。每一層可存儲的數據量是上一層的的10倍。舉個例子,第Level 2層可存儲的數據量80MB,那麼第Level 3層可存儲的數據量就是800MB。當某一層可存儲的數據量達到最大值,LevelDB就會從當層選取一個.sst文件,向下層做Compact操作,由於來自於上層的新數據,所以下層的.sst文件內容將產生變化(上文說過,.sst文件中的內容是按照數據的key排序的)。

每一個SSTable文件,由多個Block塊構成(默認大小爲4KB),block塊是LevelDB讀寫磁盤上SSTable文件的最小單元。每一個SSTable文件最後一個Block塊稱爲Index Block,它指明瞭SSTable文件中每一個Data block的起始位置。

但是每次讀取某個Block塊時,如果都在磁盤上先去找Index Block,然後再根據其中記錄的index,找到Block在文件的起始位置的話,查找效率顯然不高。所以LevelDB的內存區域中,有一個稱爲Block Cache的區域。這個區域存儲着衆多的Index Block,這樣就不需要到磁盤上查找Index Block了。

  • Manifest

那麼衆多的.sst文件是如何被管理的呢?要知道如果在衆多.sst文件中進行某條消息的查找時,如果將某一層的.sst文件全部進行遍歷,那麼性能肯定是不能接受的。在LevelDB中有一類文件被稱爲Manifest,這些Manifest文件記錄了sst文件的關鍵信息,包括(但不限於):某個.sst文件屬於哪一個Level、這個.sst文件中最小的key值、這個.sst文件中最大的key值

4-2、在ActiveMQ中配置LevelDB

在ActiveMQ中配置使用LevelDB作爲持久化存儲方案實際上很簡單,使用主配置文件中的persistenceAdapter標記就可以完成。最簡配置如下所示:

......
<broker xmlns="http://activemq.apache.org/schema/core"  brokerName="localhost" dataDirectory="${activemq.data}">
    ......
    <persistenceAdapter>
        <levelDB directory="${activemq.data}/levelDB"/>
    </persistenceAdapter>
    ......
</broker>
......

以上示例配置中,directory屬性表示LevelDB的結構文件所放置的目錄位置。請注意,由於log文件是順序寫的機制,所以log文件也會預佔磁盤空間,並且log文件默認的大小就是100MB。那麼只要生成一個log文件,就至少會佔據100MB的存儲空間(但這不代表總的已使用量)。也就是說,如果您將主配置文件中storeUsage標記的limit屬性設置爲200mb,那麼透過ActiveMQ管理界面看到的現象就是:只要有任何一條PERSISTENT Message被接受,Store percent used立刻就會變成50%。如果您將storeUsage標記的limit屬性爲100mb,那麼只要有任何一條PERSISTENT Message被接受,ActiveMQ服務端的Producer Flow Control策略就會立刻開始工作。

所以一定不要吝嗇分配memoryUsage、storeUsage。依據您的團隊在生產環境下的存儲方案,也可以通過logSize屬性改變LevelDB中單個log文件的大小。如下示例:

......
<!-- 限制成50mb -->
<persistenceAdapter>
    <levelDB directory="${activemq.data}/levelDB" logSize="52428800"/>
</persistenceAdapter>
......

上一小節我們介紹到,默認的LevelDB存儲策略中,當ActiveMQ接收到一條消息後,就會同步將這條消息寫入到log文件中,並且同時在內存區域向Memtable寫入位置索引。通過配置您也可以將這個過程改爲“異步”:

......
<!-- 改爲異步寫log文件 -->
<persistenceAdapter>
    <levelDB directory="${activemq.data}/levelDB" logSize="52428800" sync="false"/>
</persistenceAdapter>
......

以下列表展示了您可以使用的LevelDB的配置屬性,使用“*”標識出來的屬性是筆者認爲重要的配置項:

property namedefault valueComments
*directory“LevelDB”數據文件的存儲目錄
synctrue是否進行磁盤的同步寫操作
*logSize104857600 (100 MB)log日誌文件的最大值
verifyChecksumsfalse是否對從文件系統中讀取的數據進行強制校驗校驗
paranoidChecksfalse如果LevelDB檢測到數據錯誤,則儘快將錯誤在存儲位置進行標記
indexFactoryorg.fusesource.leveldbjni.JniDBFactory, org.iq80.leveldb.impl.Iq80DBFactory創建LevelDB時使用的工廠類,由於LevelDB的本質是C++程序庫,所以Java是通過Jni進行底層調用的
*indexMaxOpenFiles1000可供索引使用的打開文件的數量,這是因爲Level內部使用了多線程進行文件讀寫操作
indexWriteBufferSize6291456 (6 MB)內存MemTable的最大值,如果MemTable達到這個值,就會被標記爲Immutable
indexBlockSize4096 (4 K)每個讀取到內存的SSTable——Index Block數據的大小
*indexCacheSize268435456 (256 MB)使用一個內存區域記錄多個Level中,SSTable——Index Block數據,以便讀操作時,不經過遍歷就可直接定位數據在某個level中的位置,建議增大該區域
indexCompressionsnappy適用於索引塊的壓縮類型,影響Compression策略
logCompressionnone適用於日誌記錄的壓縮類型,影響Compression策略

5、關係型數據庫存儲方案

從ActiveMQ V4版本開始,ActiveMQ就支持使用關係型數據庫進行持久化存儲——通過JDBC實現的數據庫連接。可以支持的關係型數據庫包括(但不限於):Apache Derby、DB2、HSQL、Informix、MySQL、Oracle、Postgresql、SQLServer、Sybase。

下面向各位讀者演示如何爲ActiveMQ配置Mysql數據庫服務。前提是您已經某個網絡位置準備好了Mysql服務,並可以成功進行遠程登錄。

......
<broker>
    ......
    <!-- 配置ActiveMQ連接到Mysql服務 -->
    <!-- 記得去掉原來的KahaDB或者LevelDB的配置 -->
    <persistenceAdapter>
        <jdbcPersistenceAdapter dataSource="#mysql_datasource" createTablesOnStartup="true"/>
    </persistenceAdapter>
    ......
</broker>

<!-- 演示使用的是C3p0連接池,當然您也可以使用DBCP連接池 -->
<!-- 就是spring的配置文件結構 -->
<bean id="mysql_datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activemqdb?relaxAutoCommit=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
  <property name="user" value="root"/>
  <property name="password" value="123456"/>
  <property name="minPoolSize" value="10"/>
  <property name="maxPoolSize" value="30"/>
  <property name="initialPoolSize" value="10"/>
</bean>
......

在配置關係型數據庫作爲ActiveMQ的持久化存儲方案時,要注意幾個事項:

  • 配置信息建議放置在您的jetty.xml配置文件中,也可以放置在activemq.xml配置文件中。除此之外,還要記得需要使用到的相關jar文件放置到ActiveMQ安裝路徑下的./lib目錄。例如使用mysql + c3p0的配置中,需要的jar包至少包括:mysql-jdbc驅動的jar包和c3p0的jar包。

  • 在jdbcPersistenceAdapter標籤中,我們設置了createTablesOnStartup屬性爲true,這是爲了在第一次啓動ActiveMQ時,ActiveMQ服務節點會自動創建所需要的數據表。啓動完成後,可以去掉這個屬性,或者更改createTablesOnStartup屬性爲false。

  • 在配置和測試的過程中,您可以會遇到這樣的問題:“java.lang.IllegalStateException: BeanFactory not initialized or already closed”這是因爲您的操作系統的機器名中有“_”符號。更改機器名並且重啓後,即可解決問題。

在同樣的硬件資源條件下,相比KahaDB和LevelDB這樣的“內存+存儲介質”這樣的持久化方案而言,使用關係型數據庫作爲ActiveMQ的持久化方案絕對不能說“性能”最好,但是在大多數情況下這個持久化方案也不會成爲整個頂層架構的設計瓶頸(因爲關係型數據庫一般都有自己的熱備和負載方案)。所以很多團隊還是會使用這樣的持久化方案,很大一部分原因就是這些團隊對關係型數據庫有更豐富的使用經驗,且有專門的數據庫管理人員。

6、如何選用持久化存儲方案

以上我們介紹了三種持久化存儲方案:KahaDB、LevelDB、關係型數據庫。其中KahaDB和LevelDB的工作原理基本類似,都採用內存+磁盤介質的方案:內存用於存放信息的位置索引,磁盤介質上存放消息內容。而關係型數據庫的方案,ActiveMQ將完全通過JDBC對數據庫進行操作完成消息的存儲和修改。

但是不是如網絡上一些資料所說的那樣,一定要對三種持久化存儲方案的速度做比較後,選擇最快的那種存儲方案呢?這裏面至少有兩個誤區:

  1. 某種存儲方案的速度一定比另一種存儲方案的速度快。
  2. 一定要選擇速度快的那種存儲方案。

下面我們進行詳細的討論:

  • 根據不同的硬件層配置,同一種持久化存儲方案的性能是完全不一樣的。例如在單節點計算的情況下,選用DDR 2133雙通道內存組和DDR3 1333單通道內存條從理論上至少就可以多獲得4Gbps的帶寬;選用同樣支持SATA3規範的機械硬盤和SSD固態硬盤,雖然兩者理論上的對外速度都標稱6Gbps,但是由於機械硬盤上單磁頭的讀寫速度存在瓶頸,所以就算進行連續讀操作,速度也只能達到200MB/s左右;但是固態硬盤的連續讀速度卻可以達到500MB/s左右(基本已經接近6Gbps)。

  • 如果是企業級硬件存儲方案,那麼速度差異還會繼續擴大。例如電信行業經常採用的IBM 各個系列磁盤陣列,一般都會配置諸如RAID5這樣的軟存儲方案。這樣一來,同一份文件有多個副本,並且有多個磁頭負責讀寫。磁盤陣列的對外輸出一般會採用光纖通道(FC),而光纖通道行業協會(Fibre Channel Industry Association)最新推出的(2015年實施)Gen 6第6代光纖通道標準中,設計的對外傳輸理論速度是128Gbps。

  • 當然,除非您的公司/團隊能夠接受這些企業級存儲方案高昂的費用。否則還是建議在生產環境搭建性價比較高的折中方案。例如採用20臺左右PC Server搭建Ceph/MFS分佈式存儲系統。這個方案,我們將在下一篇文章中進行詳細說明。

所以某種存儲方案的性能,除了這種存儲方案的工作原理以外對其有直接影響外,還要考慮它的工作環境。只有根據軟件團隊預估的系統壓力、綜合建設方案、考慮後續擴容方式,來確定採用哪一種存儲方案,纔是科學的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章