Virtual Destinations

Virtual Destinations allow us to create logical destinations that clients can use to produce and consume from but which map onto one or more physical destinations . It allows us to provide more flexible loosly coupled messaging configurations.

Virtual Topics

The idea behind publish subscribe is a great one. Allow producers to be decoupled from consumers so that they do not even know how many consumers are interested in the messages they publish. The JMS specification defines support for durable topics however they suck a little as we will describe...

Why JMS durable topics suck

A JMS durable subscriber MessageConsumer is created with a unique JMS clientID and durable subscriber name. To be JMS compliant only one JMS connection can be active at any point in time for one JMS clientID, and only one consumer can be active for a clientID and subscriber name. i.e., only one thread can be actively consuming from a given logical topic subscriber. This means we cannot implement

  • failover of the subscriber if that one process running that one consumer thread dies;
  • load balancing of messages.

Now queue semantics in JMS offer the ability to load balance work across a number of consumers in a reliable way - allowing many threads, processes and machines to be used to process messages. Then we have sophisticated sticky load balancing techniques like Message Groups to load balance and parallelise work while maintaining ordering.

Another added benefit of having physical queues for each logical topic subscriber is we can them monitor the queue depths via JMX to monitor system performance together with being able to browse these physical queues.

Virtual Topics to the rescue

The idea behind virtual topics is that producers send to a topic in the usual JMS way. Consumers can continue to use the Topic semantics in the JMS specification. However if the topic is virtual, consumer can consume from a physical queue for a logical topic subscription, allowing many consumers to be running on many machines & threads to load balance the load.

E.g., let's say we have a topic called VirtualTopic.Orders . (Where the prefix VirtualTopic. indicates its a virtual topic). And we logically want to send orders to systems A and B. Now with regular durable topics we'd create a JMS consumer for clientID_A and "A" along with clientID_B and "B".

With virtual topics we can just go right ahead and consume to queue Consumer.A.VirtualTopic.Orders to be a consumer for system A or consume to Consumer.B.VirtualTopic.Orders to be a consumer for system B.

We can now have a pool of consumers for each system which then compete for messages for systems A or B such that all the messages for system A are processed exactly once and similarly for system B.

Customizing the out-of-the-box defaults

The out-of-the-box defaults are described above. Namely that the only virtual topics available must be within the VirtualTopic.> namespace and that the consumer queues are named Consumer.*.VirtualTopic.> .

You can configure this to use whatever naming convention you wish. The following example shows how to make all topics virtual topics. The example below is using the name > to indicate 'match all topics'. You could use this wildcard to apply different virtual topic policies in different hierarchies.

<beans 
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq ="http://activemq.apache.org/schema/core"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

<broker xmlns="http://activemq.apache.org/schema/core" >
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name="> " prefix="VirtualTopicConsumers.*." selectorAware="false" />
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>

</broker>

</beans>

Note that making a topic virtual does add a small CPU overhead when sending messages to the topic but it is fairly small. From version 5.4 , dispatch from virtual topics to subscription queues can be selectorAware such that only messages that match one of the existing subscribers are actually dispatched. Using this option prevents the build up of unmatched messages when selectors are used by exclusive consumers.

Composite Destinations

Composite Destinations allow for one-to-many relationships on individual destinations; the main use case is for composite queues . For example when a message is sent to queue A you may want to forward it also to queues B and C and topic D. Composite destinations are then a mapping from a virtual destination to a collection of other physical destinations. In this case the mapping is broker side and the client is unaware of the mapping between the destinations. This is different from client side Composite Destinations where the client uses a URL notation to specify the actual physical destinations that a message must be sent to.

The following example shows how to set up a <compositeQueue/> element in the XML configuration so that when a message is sent to MY.QUEUE then it is really forwarded to the physical queue FOO and the topic BAR.

<beans 
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq ="http://activemq.apache.org/schema/core"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

<broker persistent="false" useJmx="false" xmlns="http://activemq.apache.org/schema/core" >
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="MY.QUEUE" >
<forwardTo>
<queue physicalName="FOO" />
<topic physicalName="BAR" />
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>

</broker>

</beans>

You can use this technique for example to be able to watch a queue, by sending messages sent to a queue to a notification topic. Note that the default behavior of a composite destination is to forward all messages from the source (composite) destination to the target physical destinations. So, for example, if you have a composite topic that routes from a topic to a queue, then by default you won't be able to consume messages directly from the composite topic itself. Consider the following fragment:

          <compositeTopic name="IncomingTopic"
>

<forwardTo>
<queue physicalName="IncomingQueue" />
</forwardTo>
</compositeTopic>

... if messages are sent to 'IncomingTopic', then they will all be forwarded to 'IncomingQueue'; so, topic subscribers on 'IncomingTopic' will receive no messages! This may or may not be desirable, depending on your use-case. If you would like messages to consumable from the composite destination, you must set the 'forwardOnly' attribute to false, as is shown in the snippit below. This has the effect of forwarding messages to 'IncomingQueue', but keeping messages on 'IncomingTopic' for any durable or non-durable topic subscribers.

          <compositeTopic name="IncomingTopic"
 forwardOnly="false"
>

<forwardTo>
<queue physicalName="IncomingQueue" />
</forwardTo>
</compositeTopic>

Using filtered destinations

From Apache ActiveMQ 4.2 onwards you can now use selectors to define virtual destinations.

You may wish to create a virtual destination which forwards messages to multiple destinations but applying a selector first to decide if the message really does have to go to a particular destination.

The following example shows how a message sent to the virtual destination MY.QUEUE will be forwarded to FOO and BAR if the selectors match

<beans 
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq ="http://activemq.apache.org/schema/core"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />

<broker xmlns="http://activemq.apache.org/schema/core" >
<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>

</broker>

</beans>

Avoiding Duplicate Message in a Network of Brokers

You have to make sure that the messages sent to the Consumer.*.VirtualTopic.> destination are not forwarded. If you use Virtual Topics in a network of brokers, it is likely you will get duplicate messages if you use the default network configuration. This is because a network node will not only forward message sent to the virtual topic, but also the associated physical queues. To fix this, you should disable forwarding messages on the associated physical queues.

Here is an example of how to do that:

    <networkConnectors>

<networkConnector uri="static://(tcp://localhost:61617)" >
<excludedDestinations>
<queue physicalName="Consumer.*.VirtualTopic.> " />
</excludedDestinations>
</networkConnector>
</networkConnectors>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值