虛擬目的地
虛擬目的地 允許我們建立邏輯目的地,讓客戶端可以用來生產和消費訊息,但這些目的地會對應到一個或多個實體目的地。這使我們能夠提供更彈性的鬆散耦合訊息配置。
虛擬主題
發布訂閱 背後的概念很棒。它允許生產者與消費者解耦,因此他們甚至不知道有多少消費者對他們發布的訊息感興趣。JMS 規範定義了對持久主題的支援,但正如我們將描述的,它們存在限制…
JMS 持久主題的限制
一個 JMS 持久訂閱者 MessageConsumer 是以唯一的 JMS clientID 和持久訂閱者名稱建立的。為了符合 JMS 標準,在任何時間點,對於一個 JMS clientID 只能有一個 JMS 連線處於活動狀態,並且對於一個 clientID 和訂閱者名稱,只能有一個消費者處於活動狀態。也就是說,只有一個執行緒可以主動從給定的邏輯主題訂閱中消費。這意味著我們無法實現
- 訊息的負載平衡。
- 如果執行該消費者執行緒的程序死掉,則訂閱者快速失效轉移。
現在,JMS 中的佇列語義提供了在許多消費者之間以可靠的方式負載平衡工作的能力 - 允許使用許多執行緒、程序和機器來處理訊息。然後,我們擁有複雜的黏性負載平衡技術,例如 訊息群組,可以在保持順序的同時進行負載平衡和並行化工作。
為每個邏輯主題訂閱者使用實體佇列的另一個額外好處是,我們可以透過 JMX 監視佇列深度,以監控系統效能,同時能夠瀏覽這些實體佇列。
虛擬主題來救援
虛擬主題背後的概念是生產者以通常的 JMS 方式發送到主題。消費者可以繼續使用 JMS 規範中的主題語義。但是,如果主題是虛擬的,消費者可以從邏輯主題訂閱的實體佇列中消費,從而允許許多消費者在許多機器和執行緒上運行以負載平衡負載。
例如,假設我們有一個名為 VirtualTopic.Orders 的主題。(其中前綴 VirtualTopic. 表示它是一個虛擬主題)。我們邏輯上想將訂單發送到系統 A 和 B。現在,使用常規的持久主題,我們將為 clientID_A 和“A”以及 clientID_B 和“B”建立一個 JMS 消費者。
使用虛擬主題,我們可以繼續消費佇列 Consumer.A.VirtualTopic.Orders 作為系統 A 的消費者,或消費 Consumer.B.VirtualTopic.Orders 作為系統 B 的消費者。
我們現在可以為每個系統建立一個消費者池,這些消費者競爭系統 A 或 B 的訊息,以便系統 A 的所有訊息都被處理一次,系統 B 的訊息也是如此。
自訂開箱即用的預設值
上面描述了開箱即用的預設值。也就是說,唯一可用的虛擬主題必須在 VirtualTopic.> 命名空間內,並且消費者佇列命名為 Consumer.*.VirtualTopic.>。
您可以配置此設定以使用您想要的任何命名約定。以下 範例 顯示如何使所有主題都成為虛擬主題。下面的範例使用名稱 > 來表示「符合所有主題」。您可以使用此萬用字元在不同的階層中應用不同的虛擬主題策略。
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name=">" prefix="VirtualTopicConsumers.*." selectorAware="false"/>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
請注意,使主題虛擬化在向主題發送訊息時會增加少量的 CPU 負擔,但它相當小。
選項 | 預設值 | 描述 |
---|---|---|
selectorAware | false | 僅實際分派與現有訂閱者之一相符的訊息。當獨佔消費者使用選擇器時,使用此選項可防止建立不匹配的訊息 |
local | false | 當為 true 時,不要廣播透過網路接收的訊息 |
concurrentSend | false | 當為 true 時,使用執行器進行廣播,以便並行發生發送。這允許日誌批次寫入,從而減少磁碟 I/O (5.12) |
transactedSend | false | 當為 true 時,對廣播發送使用交易,以便進行單個磁碟同步。如果沒有客戶端交易,則會建立本地代理交易 (5.13) |
dropOnResourceLimit | false | 當為 true 時,忽略廣播期間擲出的任何 ResourceAllocationException(請參閱:sendFailIfNoSpace 策略項目) (5.16) |
setOriginalDestination | true | 當為 true 時,轉寄訊息上的目的地會設定為消費者佇列,並且 originalDestination 訊息屬性會追蹤虛擬主題 (5.16) |
VirtualSelectorCacheBrokerPlugin
當 selectorAware=true 時,僅考慮活動的消費者進行選擇器匹配。如果消費者斷線並重新連線,他們將會錯過訊息。selectorAware=true 的目的是不讓訊息累積。virtualSelectorCacheBrokerPlugin 提供一個快取,用於追蹤與消費者目的地相關聯的選擇器,以便他們可以在沒有該消費者的情況下應用。這樣,只有選定的訊息才會累積。可以持續儲存現有的選擇器集合,以便在重新啟動時可以恢復。外掛程式以正常方式應用於外掛程式區段。程式碼區塊
<plugins>
<virtualSelectorCacheBrokerPlugin persistFile="<some path>/selectorcache.data" />
</plugins>
注意:persistFile 選項使用 Java 序列化,應使用適當的 jdk.serialFilter(允許 ConcurrentHashMap)鎖定。
複合目的地
複合目的地允許在個別目的地之間建立一對多的關係;主要的使用案例是複合佇列。例如,當訊息發送到佇列 A 時,您可能還想將其轉寄到佇列 B 和 C 以及主題 D。然後,複合目的地是從虛擬目的地到其他實體目的地集合的對應。在這種情況下,對應是代理端,並且客戶端不知道目的地之間的對應。這與客戶端 複合目的地 不同,在客戶端複合目的地中,客戶端使用 URL 標記法來指定必須將訊息發送到其中的實際實體目的地。
以下 範例 顯示如何在 XML 配置中設定 MY.QUEUE
時,它會實際轉寄到實體佇列 FOO
和主題 BAR
。
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<queue physicalName="FOO" />
<topic physicalName="BAR" />
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
預設情況下,訂閱者無法直接從複合佇列或主題消費訊息 - 它只是一個邏輯結構。在給定上述配置的情況下,訂閱者只能從 FOO
和 BAR
消費訊息;但不能從 MY.QUEUE
消費訊息。
可以變更此行為以實作諸如透過將相同的訊息發送到通知主題(線路竊聽)來監視佇列的使用案例,方法是將選擇性設定的 forwardOnly
屬性設定為 false。
<compositeQueue name="IncomingOrders" forwardOnly="false">
<forwardTo>
<topic physicalName="Notifications" />
</forwardTo>
</compositeQueue>
發送到 IncomingOrders
的訊息都將被複製並轉寄到 Notifications
,然後再放置在實體 IncomingOrders
佇列中供訂閱者消費。
如果 forwardOnly
屬性未定義或設定為 true
,則 compositeQueue
和 compositeTopic
之間沒有邏輯差異 - 它們可以互換使用。僅當通過使用 forwardOnly
使複合目的地成為實體時,選擇 compositeTopic
/compositeQueue
才會對行為產生影響。
使用篩選的目的地
從 Apache ActiveMQ Classic 4.2 版開始,您現在可以使用選擇器來定義虛擬目的地。
您可能希望建立一個虛擬目的地,該目的地將訊息轉寄到多個目的地,但首先應用選擇器以決定訊息是否真的必須前往特定目的地。
以下範例顯示,如果選擇器符合,則發送到虛擬目的地 MY.QUEUE 的訊息將被轉寄到 FOO 和 BAR
<destinationInterceptors> <virtualDestinationInterceptor> <virtualDestinations>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<filteredDestination selector="odd = 'yes'" queue="FOO"/>
<filteredDestination selector="i = 5" topic="BAR"/>
</forwardTo>
</compositeQueue>
</virtualDestinations> </virtualDestinationInterceptor> </destinationInterceptors>
避免代理網路中出現重複訊息
TLDR:橋接消費者佇列或虛擬主題,而不是兩者。
通常,您會網路連接消費者佇列。在這種情況下,重要的是不要橋接虛擬主題上的任何普通主題消費者,因為任何轉寄的訊息都會再次廣播到網路代理上的消費者佇列,從而導致重複。
也可以橋接虛擬主題,在這種情況下,必須從任何網路連接器配置中排除消費者佇列。
以下是如何排除虛擬主題消費者佇列的範例
<networkConnectors> <networkConnector uri="static://([tcp://127.0.0.1:61617](tcp://127.0.0.1:61617))">
<excludedDestinations>
<queue physicalName="Consumer.*.VirtualTopic.>"/>
</excludedDestinations>
</networkConnector> </networkConnectors>