程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> WildFly評估之消息子系統

WildFly評估之消息子系統

編輯:關於JAVA

WildFly,前身是JBoss AS,從V8開始為區別於JBoss EAP,更名為WildFly。HornetQ是JBoss開發的一個獨立的消息中間件,被整合進WildFly作為消息子系統。

HornetQ完全支持JMS,HornetQ不但支持JMS1.1 API同時也定義屬於自己的消息API(如下圖中的Core Client),以最大限度地提升HornetQ的性能和靈活性。

圖 1 客戶程序HornetQ的兩種交互模式

關於Core Client的API介紹請參見:http://docs.jboss.org/hornetq/2.3.0.CR2/docs/api/hornetq-client/

1.1.1. 消息類型

HornetQ與JMS保持一致支持兩種消息類型:Point-to-Point和Publish/Subscribe。

Point-to-Point

圖 2 消息類型之Point-to-Point

URL:http://www.bianceng.cn/Programming/Java/201410/45833.htm

Publish/Subscribe

圖 3 消息類型之Publish/Subscribe

1.1.2. HornetQ的專用術語說明

為了能夠更好地使用WildFly的消息子系統,有必要對其專用術語做一下說明。其中有兩組概念比較重要:

Acceptors and Connectors

Invm and Netty

1.1.2.1. Acceptors and Connectors

Acceptor

指定HornetQ Server接受什麼類型的連接(Connection)

Connector

為客戶端指定連接HornetQ Server的方式

相關配置定義在standalone以及domain的profile中,以下列舉片段供參考。

<connectors>
    <netty-connector name="netty" socket-binding="messaging"  />
    <netty-connector name="netty-throughput" socket-binding="messaging-throughput">
        <param key="batch-delay" value="50"  />
    </netty-connector>
    <servlet-connector name="servlet" socket-binding="http" host="default-host"  />
    <in-vm-connector name="in-vm" server-id="0"  />
</connectors>
<acceptors>
    <netty-acceptor name="netty" socket-binding="messaging"  />
    <netty-acceptor name="netty-throughput" socket-binding="messaging-throughput">
         <param key="batch-delay" value="50"  />
        <param key="direct-deliver" value="false"  />
    </netty-acceptor>
    <in-vm-acceptor name="in-vm" server-id="0"  />
</acceptors>

1.1.2.2. Invm and Netty

Acceptor和Connector是個相對的概念,因此定義時需要成對定義。而Invm和Netty就是用來定義Client和HornetQ Server是否在同一個JVM中。Invm標識Client和HornetQ Server在同一個JVM中;Netty標識Client和HornetQ Server在不同的JVM中

1.1.3. 消息持久化

WildFly中的消息是默認做持久化並持久化到文件中(Persistent Journal, 請參見圖15客戶程序HornetQ的兩種交互模式)。文件操作有以下兩種方式:

Java Non-blocking IO (NIO)

利用Java標准的NIO API操作文件以獲取更好的性能,需要Java SE 6及更新版本。

Linux Asynchronous IO (AIO)

使用Linux的本地異步IO庫進行操作,對Linux(內核2.6及以上)系統強依賴。該方式性能優於Java NIO。

WildFly默認使用AIO進行消息持久操作,以獲取最佳性能 ,如果在不具備Linux AIO的條件下,會自動切換到Java NIO方式進行消息持久化。

圖 4 消息持久化場景模式

從上圖中可以看出,WildFly的消息子系統中消息持久化除了支持本地文件系統操作,也支持NFS,基於SAN的GFS V2共享文件系統的操作。

[注意事項]

(1) 在使用模式2或者3時,Linux AIO為唯一文件操作方式。

(2) 如果使用模式1,即每個HornetQ服務器都將消息持久化到所在主機的本地文件系統,在做HornetQ服務器的HA特性(Failoerver)時,需要做消息復制(將消息日志由主HornetQ服務器復制到從HornetQ服務器上)。

【筆者觀點】

HornetQ的消息持久化方式比較單一,沒有靈活的持久化方式(比如數據庫持久化)供用戶選擇或定制。好在提供了通過NFS或者GFS V2 on SAN進行消息持久化共享的方式,從而避免了在集群情況下由於做消息復制而造成的性能損耗。

在消息中間件負載要求過高的場景下,如果在消息持久層(文件系統)與HornetQ集群之間加入緩存集群(Infinispan)做消息共享,可以提供更好的HA特性。

1.1.4. 消息復制

消息復制是高可用性的前提功能,在集群環境中通過消息復制保持主(Master)節點和從(Slave)節點的狀態對等(消息一致),當主節點失效後,從節點能夠立刻替代主節點保證客戶應用程序的運行不受影響。在消息持久化中講到了消息持久化的3種模式,集群中的各節點在模式2(NFS)和3(GFS)的場景下可以通過共享文件系統保證消息一致;在模式1的場景下,要保證主從節點間的消息一致需要通過消息復制來實現。

圖 5 消息復制

URL:http://www.bianceng.cn/Programming/Java/201410/45833.htm

[注意事項]

在某節點被標識為從節點,並啟動後,主節點上已經有消息(persistent journal)存在的情況下,從節點首先會從主節點上同步已存在的數據,在同步完成之前無法提供容錯 功能。

1.1.5. 消息去重

試想以下兩種消息處理場景:

場景1

消息由消息發送者(message sender)到消息目標服務器(message target),目標服務器或者網絡在消息發送之後,目標服務器接受到消息之前發生故障。

場景2

消息由消息發送者(message sender)到消息目標服務器(message target),目標服務器或者網絡在消息到達目標服務器,並且由目標服務器對消息處理完成之後,目標服務器返回響應之前發生故障。

消息發送者沒有辦法對以上兩種場景進行辨別,統一做消息重新發送。對於場景2而言,同樣的消息消費了2次。這對於一些訂購系統(比如網上購物)而言,如果不做消息去重,在場景2中,對於同一件物品發生2次訂購,對於消費者而言是不可接受的。

HornetQ提供了消息去重的機制,實現思路如下圖所示:

圖 6 消息去重原理

從上圖可以看出,HornetQ的消息去重實現原理很簡單:

消息發送者為每一條消息附加帶唯一值(官方建議用UUID)的消息頭;

目標服務器在接收消息之後,處理消息之前,先從本地緩存(Duplicate ID Cahce)中查找該消息ID是否已經存在;

如果消息頭在本地緩存中已經存在則忽略該消息;如果不存在則處理該消息;

目標服務器處理完消息後在本地緩存中緩存該消息的消息頭,以供去重檢測用。

【筆者觀點】

上述的處理邏輯在一定程度上可以避免消息去重,在極端情況下(目標服務器緩存也崩潰的時候)也難以避免消息被重復處理的情況。如果要考慮到極端情況的處理,就要犧牲一定的性能特別是分布式場景下。在實際業務場景中,比如訂單系統與積分系統,支付系統,物流系統等系統間消息投遞的場景中,出於性能考慮,一般不考慮如此極端的場景。淘寶/阿裡的消息中間件(Notify與MetaQ)都沒有為極端場景做特別設計。

鑒於目前分布式緩存大行其道,比如Teracotta的BigMemory,Oracle Coherence等等,可以采用類似於統一Session管理的方案,對Duplicate ID也做統一管理,這樣集群中無論哪一個節點崩潰都可以避免消息重復消費的情況。

1.1.6. 嚴格消息順序保證

為了說明消息的順序消費的重要性,下圖中勾畫了一個網上購物的場景。

圖 7 嚴格消息順序消費場景

① A客戶訂購一台iPad 4

② 訂購消息加入消息隊列

③ A客戶取消①中訂購的iPad4

④ 取消訂購消息加入消息隊列

⑤ 從隊列中消費訂購消息

⑥ 從隊列中消費取消訂購消息

⑦ 往數據庫中寫入訂購消息

⑧ 從數據庫中刪除訂購消息

如果⑦和⑧的處理順序顛倒,將導致客戶的訂購沒有取消成功。

如何保證消息消費的順序呢?

JMS規范(截至JMS2.0)僅僅對“一個生產者,一個QUEUE,一個消費者”的場景做了“消息的發送順序必須與消費順序嚴格一致”的規定,但對於分布式環境中,沒有對消息發送與接受的順序一致做強制要求。因此嚴格順序保證依賴各消息中間件提供商的具體實現。

IBM的WebSphere MQ中的消息分組與Oracle的WebLogic JMS的Message Unit-of-Order都可以解決上述場景中的問題。HornetQ也提供了解決方案:Message Grouping。

1.1.6.1. Message Grouping

Message Grouping通過將同一業務類型的消息分為一組,確保該組中的所有消息被同一個消費者消費(即使在集群環境中),從而確保消息能夠被順序消費。通過HornetQ的Message Grouping圖21的消息消費路由將變成(如下圖所示)。

圖 8 采用Message Grouping 後的消息順序消費

【筆者觀點】

HornetQ的Message Grouping方案有以下前提:

Queue中消息順序是正確的。即需要消息發送端意識到消息的先後順序

消費端不可以使用多線程去處理消息。

另外需要注意的是,集群環境中由於負載均衡消息可能分布在不同的Queue上面,這種情況下HornetQ也難以保證消息消費順序的正確性。當然可以通過修改負載均衡算法,借助類似於sticky session的技術將來自於同一session的消息,都發往同一個HornetQ服務器上的同一個Destination。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved