// source:
// translate: liuzhengyi
// since 2014-06-13
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 loosely 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 have limitations as we will describe...
发布订阅背后的思想是很强大的。将生产者和消费者解耦,生产者甚至不知道多少消费者对它们产生的消息感兴趣。
JMS规范定义了对持久化 topics 的支持,但是我们下面将描述一些它的局限性……
== The limitations of JMS durable topics ==
== JSM持久topics的局限 ==
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
* load balancing of messages.
* fast failover of the subscriber if that one process running that one consumer thread dies.
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.
JMS持久化订阅者 MessageConsumer 在创建时会生成一个 唯一的 JMS clientID 和一个持久化订阅者的name。 为了符合JMS,对于一个JMSclientID,同一时间,只能有一个活跃的JMS连接,并且,对于一个clientID和订阅者name而言,只有一个消费者可以是活跃的。也就是说,对于给定的topic订阅者来说,只能由一个活跃的进程进行消费。这意味着我们不能实现
* 消息的负载均衡
* 当一个消费者进程死亡时 订阅者的快速故障转移
如今,JMS中的队列语义提供了通过多个消费者来实现可靠的负载均衡——允许多线程,进程和机器来处理消息。然后我们就有了成熟而复杂的负载均衡技术,就像Messge Groups一样在维护订单时进行负载均衡和并行化处理。
每一个逻辑主题的订阅者拥有一个物理队列的另外一个附加的好处是我们可以通过JMX监控队列的深度,进一步监控系统的性能,同时还能浏览这些物理队列。
== 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.
virtual topic背后的思想是生产者用正常的JMS方式把消息发送到一个topic。消费者可以继续使用JMS标准中的Topic语义。然而,如果 topic 是虚拟的, 订阅一个逻辑topic的消费者可以从一个真实队列中消费,允许多个消费者分布在多个机器上 且 多线程的进行负载均衡。
例如,假定我们现在有一个叫做 VirtualTopic.Orders 的topic。(前缀 VirtualTopic. 表明这是一个 virtual topic)。我们希望将orders 发送到 系统A 和 系统B。于是,用通常的 surable topics ,我们需要为 clientID_A 和 "A" 创建一个JMS消费者,同时为 clientID_B 和 "B"创建一个JMS消费者。
有了virtual topics 我们可以直接从队列 Consumer.A.VirtualTopic.Orders 中为 系统A 消费,或者从队列 Consumer.B.VirtualTopic.Orders 中为 系统B 消费。
我们现在可以有一个为每个系统准备的消费者池,它们可以全部用来消费系统A或者B的消息,系统A的所有消息都被精确处理了一次,系统B也一样。
== Customizing the out-of-the-box defaults ==
== 自定义 out-of-the-box 默认值 ==
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.
上面已经描述了 out-of-the-box defaults。 即有效的 virtual topics 必须在 VirtualTopic.> 命名空间内,而且消费者队列必须以 Consumer.*.VirtualTopic.>. 命名。
你可以通过配置来使用任何你想用的命名习惯。接下来的示例展示了如何将所有的topics变成 virtual topics。下面的例子使用了名称 > 来表示“匹配所有的topics”。你可以使用这个通配符在不同的层级应用不同的 virtual topic 策略。
xmlns:amq=""
xmlns:xsi=""
xsi:schemaLocation=" /spring-beans-2.0.xsd
/activemq-core.xsd">
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.
请注意,将一个topic设为 virtual 确实会在发送消息消息到topic时增加一点CPU开销,但这时非常小的影响。从5.4版开始,从 virtual topics 分发消息到订阅者队列 ?? can be selectorAware ,只有匹配到一个真实存在的的订阅者的消息才会被真正分发。使用这个选项可以防止当 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
复合目的地允许在多个独立的目的地之间建立 一对多 的关系;主要的使用案例是用于复合队列。例如,一条消息被发送到队列A,你可能会希望转发这条消息到队列B,队列C和 topic D。复合目的地此时就是一个从一个虚拟目的地到一系列其他真实目的地的映射。在这个例子中,这种映射时服务器端的,客户端并不知道目的地之间的映射。 这不同于客户端的 复合目的地(?客户端使用URL表示法来指定一条消息必须被发送至的实际目的地?)
下面的示例展示乐如何在XML配置文件中设置一个
xmlns:amq=""
xmlns:xsi=""
xsi:schemaLocation=" /spring-beans-2.0.xsd
/activemq-core.xsd">
By default, subscribers cannot consume messages directly from a composite queue or topic - it is a logical construct only. Given the configuration above, subscribers can only consume messages from FOO and BAR; but not MY.QUEUE.
This behaviour can be altered to implement use cases such as watching a queue by sending the same messages to a notification topic (wire tapping), by setting the optionally set forwardOnly attribute to false.
默认情况下,订阅者不能直接从一个复合队列或topic中消费消息 —— 这只是逻辑上的结构。使用上面的配置,订阅者只能从 FOO 和 BAR 中消费消息,但是不能从 MY.QUEUE中消费。
这种行为可以改变 ?? This behaviour can be altered to implement use cases such as watching a queue by sending the same messages to a notification topic (wire tapping), by setting the optionally set forwardOnly attribute to false. ??
Messages sent to IncomingOrders will all be copied and forwarded to Notifications, before being placed on the physical IncomingOrders queue for consumption by subscribers.
Where the forwardOnly attribute is not defined or is set to true, there is no logical difference between a compositeQueue and a compositeTopic - they can be used interchangeably. It is only when a composite destination is made physical through the use of forwardOnly that the choice of compositeTopic/compositeQueue has an impact on behavior.
发送到 IncomingOrders 的消息 在放置到真实的 IncomingOrders 队列给订阅者消费之前 将全部被复制并转发到 Notifications。
forwardOnly 属性未定义 或者设为 true,复合Queue 和 复合Topic 逻辑上并没有区别 —— 它们可以混用。只有当使用forwardOnly 将复合目的地变得physical,选择 复合Topic或者复合Queue才会对行为有影响。
== 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
从Apache ActiveMQ4.2之后,你可以使用 selector 来定义虚拟目的地。
你也许希望创建在将消息转发到多个目的地之前先应用一个selector来判断这条消息是否真的需要发送到特定目的地的虚拟目的地。
下面这个示例,展示了一条发送虚拟目的地 MY.QUEUE 的消息是如何在匹配 selectors 时转发到 FOO和BAR的。
xmlns:amq=""
xmlns:xsi=""
xsi:schemaLocation=" /spring-beans-2.0.xsd
/activemq-core.xsd">
== Avoiding Duplicate Message in a Network of Brokers ==
== 在broker网络中避免重复消息 ==
You have to make sure that the messages sent to the Consumer.*.VirtualTopic.> destination are not forwarded when you're using both queue-based and non-queue based subscribers to the virtual topic (that is, if you have normal topic subscribers to the virtual topic). 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.
当你同时使用 queue-based 和 non-queue-based 订阅者订阅 virtual topic(也就是说,你有普通的topic订阅者订阅 virtual topic)时,你必须确保发送到 Consumer.*.VirtualTopic.> 目的地的消息不被转发。如果你在borkers 网络中使用 Virtual Topics,如果你又使用来默认的网络配置,你很可能会收到重复消息。这是因为一个网络节点不仅会转发发送到 virtual topic 的消息,也会转发 绑定的物理队列。 要修复这个问题,你需要在绑定的物理队列上禁用转发消息。
下面是一个相关的示例:
Here is an example of how to do that: