天天看點

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

(接上文《架構設計:系統間通信(26)——ActiveMQ叢集方案(上)》)

3、ActiveMQ熱備方案

ActiveMQ熱備方案,主要保證ActiveMQ的高可用性。這種方案并不像上節中我們主要讨論的ActiveMQ高性能方案那樣,同時有多個節點都處于工作狀态,也就是說這種方案并不提高ActiveMQ叢集的性能;而是從叢集中的多個節點選擇一個,讓其處于工作狀态,叢集中其它節點則處于待命狀态。當主要的工作節點由于各種異常情況停止服務時,保證處于待命的節點能夠無縫接替其工作。

3-1、ActiveMQ高性能方案的不足

那麼有的讀者可能會問,既然ActiveMQ的高性能方案中多個節點同時工作,在某個節點異常的情況下也不會影響其他節點的工作。這樣看來,ActiveMQ的高性能方案已經避免了單點故障,那麼我們為什麼還需要讨論ActiveMQ的高可用方案呢?

為了回答這個問題,我們先回過頭來看看ActiveMQ高性能方案的一些不足。假設如下的場景:ActiveMQ A和AcitveMQ B兩個服務節點已建立了Network Bridge;并且Producer1 連接配接在ActiveMQ A上,按照一定周期發送消息(隊列名:Queue/testC);但是目前并沒有任何消費者Consumer連接配接在任何ActiveMQ服務節點上接收消息。整個場景如下圖所示:

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

在發送了若幹消息後,我們檢視兩個節點ActiveMQ服務節點的消息情況,發現ActiveMQ A并沒有把隊列Queue/testC中的消息同步到ActiveMQ B。原來AcitveMQ Network Bridge的工作原則是:隻在服務節點間傳輸需要傳輸的消息,這樣做的原因是為了盡量減少AcitveMQ叢集網絡中不必要的資料流量。在我們實驗的這種情況下并沒有任何消費者在任何ActiveMQ服務節點上監聽/訂閱隊列Queue/testC中的消息,是以消息并不會進行同步。

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

(ActiveMQ A節點中的Queue/testC隊列中有10條消息)

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

(這些消息并沒有被實時傳輸ActiveMQ B節點中,因為B節點中并沒有任何消費者監聽/訂閱這個隊列中的消息)

那麼這樣的工作機制帶來的問題是,當沒有任何消費者在任何服務節點訂閱ActiveMQ A中隊列的消息時,一旦ActiveMQ A由于各種異常退出,後來的消費者就再也收不到消息,直到ActiveMQ A恢複工作。是以我們需要一種高可用方案,讓某一個服務節點能夠7 * 24小時的穩定提供消息服務。

3-2、基于共享檔案系統的熱備方案

3-2-1、方案介紹

基于共享檔案系統的熱備方案可以說是ActiveMQ消息中間件中最早出現的一種熱備方案。它的工作原理很簡單:讓若幹個ActiveMQ服務節點,共享一個檔案系統。當某一個ActiveMQ服務搶占到了這個檔案系統的操作權限,就給檔案系統的操作區域加鎖;其它服務節點一旦發現這個檔案系統已經被加鎖(并且鎖不屬于本程序),就會自動進入Salve模式。

ActiveMQ早期的檔案存儲方案、KahaDB存儲方案、LevelDB存儲方案都支援這個工作模式。當某個ActiveMQ節點擷取了檔案系統的操作權限後,首先做的事情就是從檔案系統中恢複記憶體索引結構:KahaDB恢複BTree結構;LevelDB恢複memTable結構。

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

因為本專題講解的技術體系都是工作在Linux作業系統上,是以為多個ActiveMQ提供共享檔案系統方案的第三方檔案系統都必須支援POSIX協定,這樣Linux作業系統才能實作遠端挂載。

幸運的是,這樣的第三方系統多不勝舉,例如:基于網絡檔案存儲的NFS、NAS;基于對象存儲的分布式檔案系統Ceph、MFS、Swift(不是ios的程式設計語言)、GlusterFS(高版本);以及ActiveMQ官方推薦的網絡塊存儲方案:SAN(就是成本有點高)。

我會在我另外一個專題——“系統存儲”中,和大家深入讨論這些存儲方案在性能、維護、擴充性、可用性上的不同。為了講解簡單,我們以下的講解采用NFS實作檔案系統的共享。NFS技術比較成熟,在很多業務領域都有使用案例。如果您的業務生産環境還沒有達到滴滴、大衆點評、美團那樣對檔案存儲性能上的要求,也可以将NFS用于生産環境。

3-2-2、執行個體參考

下面我們來示範兩個ActiveMQ節點建立在NFS網絡檔案存儲上的 Master/Salve方案。關于怎麼安裝NFS軟體就不進行介紹了,畢竟本部分内容的核心還是消息服務中間件,不清楚NFS安裝的讀者可以自行百度/Google。

以下是我們示範環境中的IP位置和功能:

IP位置 作用
192.168.61.140 NFS檔案服務
192.168.61.139 獨立的 ActiveMQ 節點
192.168.61.138 另一個獨立的 ActiveMQ 節點

關于NFS的安裝和使用我也會在另一個新的專題中進行介紹。

  • 首先為兩個ActiveMQ節點挂載NFS服務
-- 在上設定的NFS共享路徑為/usr/nfs 挂載到和的/mnt/mfdir/路徑下
-- 和上記得要安裝nfs-utils的用戶端子產品
mount .:/usr/nfs /mnt/mfdir/
           

挂在後,可以通過df指令查詢挂載在後的結果:

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

從上圖中可以看到,192.168.61.140上提供的NFS共享目錄通過mount指令挂載成為了138和139兩個實體機上的本地磁盤路徑。

  • 然後更改138和139上ActiveMQ的主配置檔案,主要目的是将使用的KahaDB/LevelDB的主路徑設定為在共享檔案系統的相同位置:
......
<persistenceAdapter>
    <!--
    這裡使用KahaDB,工作路徑設定在共享路徑的kahaDB檔案夾下
    138和139都設定為相同的工作路徑
    -->
    <kahaDB directory="/mnt/mfdir/kahaDB"/>
</persistenceAdapter>
......
           
  • 然後同時啟動138和139上的ActiveMQ服務節點。這時我們可以看到某個節點出現以下的提示資訊(記得是通過console模式進行觀察):
......
jvm 1    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[/mnt/mfdir/kahaDB]
jvm 1    |  INFO | Database /mnt/mfdir/kahaDB/lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired
......
           

在本文的示範環境中,出現以上提示的是工作在139上的ActiveMQ服務節點。這說明這個節點發現主工作路徑已經被其他ActiveMQ服務節點鎖定了,是以自動進入了Slave狀态。另外這還說明,另外運作在128實體機上的ActiveMQ服務搶占到了主目錄的操作權。

接下來我們将工作在138上的ActiveMQ服務節點停止工作,這時139上的ActiveMQ Slave服務節點自動切換為Master狀态:

......
jvm 1    |  INFO | KahaDB is version 6
jvm 1    |  INFO | Recovering from the journal @1:47632
jvm 1    |  INFO | Recovery replayed 53 operations from the journal in 0.083 seconds.
jvm 1    |  INFO | PListStore:[/usr/apache-activemq-5.13.1/bin/linux-x86-64/../../data/activemq2/tmp_storage] started
jvm 1    |  INFO | Apache ActiveMQ 5.13.1 (activemq2, ID:vm2-46561-1461220298816-0:1) is starting
......
           

從以上的提示可以看到,139上的ActiveMQ節點在自己的記憶體區域恢複了KahaDB的索引資訊,并切換為Master狀态繼續工作。需要注意的是,在139上的ActiveMQ節點切換為Master狀态後,就算之前138上的ActiveMQ節點重新恢複工作,後者也不會再獲得主目錄的操作權限,隻能進入Salve狀态。

3-3、基于共享關系型資料庫的熱備方案

基于關系型資料庫的熱備方案它的工作原理實際上和基于共享檔案系統的熱備方案相似:

  • 首先使用關系型資料庫作為ActiveMQ的持久化存儲方案時,在指定的資料庫中會有三張資料表:activemq_acks,activemq_lock,activemq_msgs(有的情況下您生成的資料表名會是大寫的,這是因為資料庫自身設定的原因)。
  • 其中“activemq_lock”這張資料表記錄了目前對資料庫擁有操作權限的ActiveMQ服務的ID資訊、Name資訊。各個ActiveMQ服務節點從這張資料表識别目前哪一個節點是Master狀态。
  • 當需要搭建熱備方案時,兩個或者更多的ActiveMQ服務節點共享同一個資料服務。首先搶占到資料庫服務的ActiveMQ節點,會将資料庫中“activemq_lock”資料表的Master狀态标記為自己,這樣其它ActiveMQ服務節點就會進入Salve狀态。

在我的之前的文章:《架構設計:系統間通信(24)——提高ActiveMQ工作性能(下)》第7-5節已經向讀者講述了如何進行ActiveMQ服務的資料庫存儲方案的配置,這裡就不再進行贅述。隻需要将每個ActiveMQ服務節點的資料庫連接配接設定成相同的位置,即可完成該熱備方案的配置工作。

為了便于各位讀者進行這種方案的配置實踐,這裡給出了關鍵的配置資訊:實際上真的很簡單,一定要首先確定您的資料庫是可用的,并且每一個ActiveMQ節點都這樣配置:

......
<broker xmlns="http://activemq.apache.org/schema/core">
    ......
    <persistenceAdapter>
        <!-- 設定使用的資料源 -->
        <jdbcPersistenceAdapter dataSource="#mysql-ds"/>
    </persistenceAdapter>
    ......
</broker>
......

<!-- 一定要確定資料庫可用,且在ActiveMQ的lib目錄中有必要的jar檔案 -->
<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://您的mysql連接配接url資訊?relaxAutoCommit=true"/>
    <property name="username" value="activemq"/>
    <property name="password" value="activemq"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>
           

3-4、LevelDB + Zookeeper的熱備方案

從ActiveMQ V5.9.0+ 版本開始,ActiveMQ為使用者提供了一種新的Master/Salve熱備方案。這個方案中,我們可以讓每個節點都有自己獨立的LevelDB資料庫(不是像3-2小節那樣共享LevelDB的工作目錄),并且使用Zookeeper叢集控制多個ActiveMQ節點的工作狀态,完成Master/Salve狀态的切換。工作模式如下圖所示(摘自官網):

架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

在這種新的工作模式下,Master節點和各個Salve節點通過Zookeeper進行工作狀态同步,即使某個Salve節點新加入也沒有問題。下面我們一起來看看如何使用LevelDB + Zookeeper的熱備方案。下表中是我們将要使用的IP位置和相關位置的工作任務:

IP位置 作用
192.168.61.140 單節點狀态的zookeeper服務
192.168.61.139 獨立的 ActiveMQ 節點
192.168.61.138 另一個獨立的 ActiveMQ 節點

關于zookeeper,在我另外幾篇文章中有相關介紹(《hadoop系列:zookeeper(1)——zookeeper單點和叢集安裝》),在這裡的示範執行個體中我們使用單節點的ZK工作狀态(但是正式生産環境中,建議至少有三個zookeeper節點)。zookeeper的安裝過程就不在本小節中進行贅述了。

  • 首先更改139和138上工作的ActiveMQ服務節點,讓它們使用獨立的LevelDB,并且都連接配接到zookeeper服務。
......
<!--
注意無論是master節點還是salve節點,它們的brokerName屬性都必須一緻
否則activeMQ叢集就算連接配接到了zookeeper,也不會把他們當成一個Master/Salve組
-->
<broker xmlns="http://activemq.apache.org/schema/core"  brokerName="activemq" dataDirectory="${activemq.data}">
    ......
    <persistenceAdapter>
        <replicatedLevelDB
                directory="/usr/apache-activemq-5.13.1/data/levelDB"
                replicas="1"
                bind="tcp://0.0.0.0:61615"
                zkAddress="192.168.61.140:2181"
                zkPath="/activemq/leveldb"/>
    </persistenceAdapter>
    ......
</broker>
......
           

我們介紹一下以上配置段落所使用的一些重要屬性。通過LevelDB + Zookeeper組建的熱備方案中肯定會有一個ActiveMQ節點充當Master節點,至于有多少個Salve節點就可以根據讀者所在團隊、項目、需求等因素來綜合考慮了。這些Master節點和Salve節點的主配置檔案中設定的brokerName屬性必須一緻,否則activeMQ叢集就算連接配接到了zookeeper,也不會把他們當成一個Master/Salve組。

directory屬性是LevelDB的基本設定,表示目前該節點使用的LevelDB所在的主工作目錄,由于每個節點都有其獨立運作的LevelDB,是以各個節點的directory屬性設定的目錄路徑可以不一樣。但是根據對正式環境的管理經驗,建議還是将每個節點的directory屬性設定成相同的目錄路徑,友善進行管理。

對于replicas屬性,官方給出的解釋如下:

The number of nodes that will exist in the cluster. At least (replicas/2)+1 nodes must be online to avoid service outage.(default:3)

這裡的“number of nodes”包括了Master節點和Salve節點的總和。換句話說,如果您的叢集中一共有3個ActiveMQ節點,且隻允許最多有一個節點出現故障。那麼這裡的值可以設定為2(當然設定為3也行,因為整型計算中 3 / 2 + 1 = 2)。但如果您将replicas屬性設定為4,就代表不允許3個節點的任何一個節點出錯,因為:(4 / 2) + 1 = 3,也就是說3個節點是本叢集能夠允許的最小節點數。

一旦zookeeper發現目前叢集中可工作的ActiveMQ節點數小于所謂的“At least (replicas/2)+1 nodes”,在ActiveMQ Master節點的日志中就會給出提示:“Not enough cluster members connected to elect a master.”,然後整個叢集都會停止工作,直到有新的節點連入,并達到所規定的“At least (replicas/2)+1 nodes”數量。

bind屬性指明了當本節點成為一個Master節點後,通過哪一個通訊位置進行和其它Salve節點的消息複制操作。注意這裡配置的監聽位址和端口不能在transportConnectors标簽中進行重複配置,否則節點在啟動時會報錯。

When this node becomes a master, it will bind the configured address and port to service the replication protocol. Using dynamic ports is also supported.

zkAddress屬性指明了連接配接的zookeeper服務節點所在的位置。在以上執行個體中由于我們隻有一個zookeeper服務節點,是以隻配置了一個位置。如果您有多個zookeeper服務節點,那麼請依次配置這些zookeeper服務節點的位置,并以“,”進行分隔:

由于zookeeper服務使用樹形結構描述資料資訊,zkPath屬性就是設定整個ActiveMQ 主/備方案叢集在zookeeper存儲資料資訊的根路徑的位置。當然這個屬性有一個預設值“/default”,是以您也可以不進行設定。

  • 在完成138和139兩個節點的ActiveMQ服務配置後,我們同時啟動這兩個節點(注意,為了觀察ActiveMQ的日志請使用console模式啟動)。在其中一個節點上,可能會出現以下日志資訊:
......
jvm     |  INFO | Opening socket connection to server /:
jvm     |  INFO | Socket connection established to /:, initiating session
jvm     |  INFO | Session establishment complete on server /:, sessionid = , negotiated timeout = 
jvm     |  INFO | Not enough cluster members have reported their update positions yet.
jvm     |  INFO | Promoted to master
jvm     |  INFO | Using the pure java LevelDB implementation.
jvm     |  INFO | Apache ActiveMQ  (activemq, ID:vm2---:) is starting
jvm     |  INFO | Master started: tcp://activemq2:61615
......
           

從以上的日志可以看到,這個節點連接配接上了zookeeper,并且分析了目前zookeeper上已連接配接的其它節點狀态後(實際上這個時候,還沒有其它節點進行連接配接),将自己“提升為Master”狀态。在另外一個AcitveMQ的節點日志中,讀者可以發現另一種形式的日志提示,類似如下:

......
jvm 1    |  INFO | Opening socket connection to server 192.168.61.140/192.168.61.140:2181
jvm 1    |  INFO | Socket connection established to 192.168.61.140/192.168.61.140:2181, initiating session
jvm 1    |  INFO | Session establishment complete on server 192.168.61.140/192.168.61.140:2181, sessionid = 0x1543b74a86e0005, negotiated timeout = 4000
jvm 1    |  INFO | Slave started
......
           

從日志中可以看到,這個節點成為了一個Slave狀态的節點。

  • 接下來我們嘗試停止目前Master節點的工作,并且觀察目前Salve節點的狀态變化。注意,如上文所述,replicas屬性的值一定要進行正确的設定:如果當Master節點停止後,目前還處于活動狀态的節點總和小于“(replicas/2)+1”,那麼整個叢集都會停止工作!
......
jvm     |  INFO | Not enough cluster members have reported their update positio ns yet.
jvm     |  INFO | Slave stopped
jvm     |  INFO | Not enough cluster members have reported their update positio ns yet.
jvm     |  INFO | Promoted to master
jvm     |  INFO | Using the pure java LevelDB implementation.
jvm     | Replaying recovery log: % done (,/,, bytes) @  , kb/s,  secs remaining.
jvm     | Replaying recovery log: % done (,,/,, bytes) @   kb/s,  secs remaining.
jvm     | Replaying recovery log: % done (,,/,, bytes)  @ , kb/s,  secs remaining.
jvm     | Replaying recovery log: % done (,,/,, bytes)  @ , kb/s,  secs remaining.
jvm     | Replaying recovery log: % done
jvm     |  INFO | Master started: tcp://activemq1:61615
......
           

從以上的日志片段可以看到,Salve節點接替了之前Master節點的工作狀态,并恢複之前已同步的LevelDB檔案到本節點的本地記憶體中,繼續進行工作。

我們在這篇文章做的示範隻有兩個ActiveMQ服務節點和一個Zookeeper服務節點,主要是為了向讀者介紹ActiveMQ下 LevelDB + Zookeeper的高可用方案的配置和切換過程,說明ActiveMQ高可用方案的重要性。實際生産環境下,這樣的節點數量配置會顯得很單薄,特别是zookeeper服務節點隻有一個的情況下是不能保證整個叢集穩定工作的。正式環境下, 建議至少使用三個zookeeper服務節點和三個ActiveMQ服務節點,并将replicas屬性設定為2。

3-5、ActiveMQ用戶端的故障轉移

以上三種熱備方案,都已向各位讀者介紹。細心的讀者會發現一個問題,因為我們沒有使用類似Keepalived那樣的第三方軟體支援浮動IP。那麼無論以上三種熱備方案的哪一種,雖然服務端可以無縫切換提供連續的服務,但是對于用戶端來說連接配接伺服器的IP都會發生變化。也就是說用戶端都會因為連接配接異常脫離正常工作狀态。

為了解決這個問題,AcitveMQ的用戶端連接配接提供了配套的解決辦法:連接配接故障轉移。開發人員可以預先設定多個可能進行連接配接的IP位置(這些位置不一定同時都是可用的),ActiveMQ的用戶端會從這些連接配接位置選擇其中一個進行連接配接,當連接配接失敗時自動切換到另一個位置連接配接。使用方式類似如下:

......
//這樣的設定,即使在發送/接收消息的過程中出現問題,用戶端連接配接也會進行自動切換
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("failover:(tcp://192.168.61.138:61616,tcp://192.168.61.139:61616)");
......
           

這樣的連接配接設定下,用戶端的連接配接就可以随服務端活動節點的切換完成相應的轉換。

4、形成生産環境方案

ActiveMQ中主要的高性能、高可用方案到此就為各位讀者介紹完了。可以看到在ActiveMQ單個節點性能配置已優化的前提下,ActiveMQ叢集的高性能方案可能會出現節點失效消息服務停止的情況(請參見3-1小節所述);而ActiveMQ叢集的高可用性方案中,由于一次隻有一個節點是Master狀态可以提供消息服務外,其他Salve節點都不能提供服務,是以并不能提高整個ActiveMQ叢集的性能。

因為兩種方案都有其限制因素,是以在實際工作中将ActiveMQ應用到生産環境時,除非您的業務環境有特殊要求的情況,一般建議将ActiveMQ的高性能方案和高可用方案進行結合。以下向各位讀者提供一種ActiveMQ高性能和高可用性結合的方案:

  • 将9個ActiveMQ節點分為三組(brokerName1、brokerName2、brokerName3),每組都有三個ActiveMQ服務節點。另外準備三個節點的zookeeper服務叢集,所有三個組九個ActiveMQ服務都共享這三個zookeeper服務節點(隻是每組ActiveMQ所設定的zkPath屬性不一樣)
  • 将每組的三個ActiveMQ服務節點做LevelDB + Zookeeper的熱備方案(且設定replicas=2)。保證每組隻有一個節點在一個時間内為Master狀态。這樣整個叢集中的九個ActiveMQ服務節點就同時會有三個ActiveMQ服務節點處于Master狀态。
  • 将整個叢集中所有ActiveMQ服務節點的高性能方案設定為“多點傳播發現”,并都采用一個相同的多點傳播位址(可以采用預設的多點傳播位址)。這樣三個處于Master狀态的ActiveMQ服務節點就會形成一個高性能方案(處于Salve狀态的節點不會發出多點傳播消息)。整個設計結構如下圖所示:
架構設計:系統間通信(26)——ActiveMQ叢集方案(下)3、ActiveMQ熱備方案4、形成生産環境方案5、下文介紹

5、下文介紹

下文我們将介紹出ActiveMQ消息中間件以外的,業界流行的其它中間件産品(包括RabbitMQ、Kafka和ZeroMQ)。由于篇幅的問題,這些中間件産品我們隻做概要新介紹(本來原計劃是要詳細介紹RabbitMQ的,隻有以後有時間再補上咯)。之後,我将和讀者一起讨論如何基于中間件技術解決生産過程中遇到的實際問題。整個消息隊列的知識介紹就暫告一段落。

繼續閱讀