150 lines
10 KiB
XML
150 lines
10 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!-- ============================================================================= -->
|
||
<!-- Copyright © 2009 Red Hat, Inc. and others. -->
|
||
<!-- -->
|
||
<!-- The text of and illustrations in this document are licensed by Red Hat under -->
|
||
<!-- a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). -->
|
||
<!-- -->
|
||
<!-- An explanation of CC-BY-SA is available at -->
|
||
<!-- -->
|
||
<!-- http://creativecommons.org/licenses/by-sa/3.0/. -->
|
||
<!-- -->
|
||
<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation -->
|
||
<!-- of it, you must provide the URL for the original version. -->
|
||
<!-- -->
|
||
<!-- Red Hat, as the licensor of this document, waives the right to enforce, -->
|
||
<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent -->
|
||
<!-- permitted by applicable law. -->
|
||
<!-- ============================================================================= -->
|
||
<chapter id="message-grouping">
|
||
<title>消息分组</title>
|
||
<para>消息组是具有下列特性的消息集合:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>在一个消息组中的消息有相同的组标识(id),即它们的<literal>JMSXGroupID</literal>(JMS)或
|
||
<literal>_HQ_GROUP_ID</literal>(ActiveMQ核心)的值相同。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>不管存在多少个接收者(consumer),一个消息组的所有消息总是被同一个接收者所接收。一个组id总是
|
||
与同一个接收者相关联。如果这个接收者被关闭,另外一个接收者就被选中来代替它接收该消息组的消息。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>消息组在需要同一个接收者按顺序处理某类消息的时候很有用。</para>
|
||
<para>一支股票的订购就是一个例子。某支股票的订单需要同一个接收者按顺序处理。于是可以每支股票有一个接收者
|
||
来处理(也可以用少一些的接收者),然后将每支股票的名字设在消息的_HQ_GROUP_ID参数中。</para>
|
||
<para>这样可以保证一支股票的消息只被同一个接收者处理。</para>
|
||
<section>
|
||
<title>使用核心接口</title>
|
||
<para>用来标识一个消息组的参数是 <literal>"_HQ_GROUP_ID""</literal> (或者相应的常量<literal
|
||
>MessageImpl.HDR_GROUP_ID</literal>)。另一种方法是在<literal>SessionFactory</literal>
|
||
中将<literal>autogroup</literal>设置为true。这样做的话组id是随机给出的。</para>
|
||
</section>
|
||
<section id="message-grouping.jmsconfigure">
|
||
<title>使用JMS</title>
|
||
<para>用来标识一个消息组的参数是<literal>JMSXGroupID</literal>。</para>
|
||
<programlisting>
|
||
// send 2 messages in the same group to ensure the same
|
||
// consumer will receive both
|
||
Message message = ...
|
||
message.setStringProperty("JMSXGroupID", "Group-0");
|
||
producer.send(message);
|
||
|
||
message = ...
|
||
message.setStringProperty("JMSXGroupID", "Group-0");
|
||
producer.send(message);
|
||
</programlisting>
|
||
<para>另一个方法是将<literal>ActiveMQConnectonFactory</literal>的<literal>autogroup</literal>
|
||
属性设为true,或者在<literal>hornetq-jms.xml</literal>文件中进行配置:</para>
|
||
<programlisting><connection-factory name="ConnectionFactory">
|
||
<connectors>
|
||
<connector-ref connector-name="netty-connector"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
<autogroup>true</autogroup>
|
||
</connection-factory></programlisting>
|
||
<para>还可以通过连接工厂来设置组id。来自这个连接工厂的所有的发送者(producer)发送的消息的<literal
|
||
>JMSXGroupID</literal>将具有指定的值。这种方法需要在<literal>hornetq-jms.xml</literal>
|
||
文件中作如下配置:
|
||
<programlisting>
|
||
<connection-factory name="ConnectionFactory">
|
||
<connectors>
|
||
<connector-ref connector-name="netty-connector"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
</entries>
|
||
<group-id>Group-0</group-id>
|
||
</connection-factory>
|
||
</programlisting></para>
|
||
</section>
|
||
<section>
|
||
<title>例子</title>
|
||
<para>参见<xref linkend="examples.message-group"/>。这个例子展示的是在JMS中如何配置与使用消息组。</para>
|
||
</section>
|
||
<section>
|
||
<title>例子</title>
|
||
<para><xref linkend="examples.message-group2"/>是另外一个消息组的例子,在这个例子中通过配置连接工厂
|
||
来使用消息组。</para>
|
||
</section>
|
||
<section>
|
||
<title>集群中的消息组</title>
|
||
<para>在集群中使用消息组是相对比较复杂的。这是因在在集群中,一个消息组中的消息有可能被送到集群中的任一全节点,
|
||
这就要求每个节点都要知道这个消息是属于哪个节点上的哪个接收者(consumer)。一个消息组的消息往往会被发送到
|
||
集群中的一个节点,而该消息组的接收者在另一个节点上。每个节点都要知道这些细节以便能将消息正确路由到所属接收
|
||
者所在的节点上。</para>
|
||
<para>为了解决上述问题,我们使用了消息组处理器。每个节点都有一个自己的消息组处理器。当一个带有组id的消息收到时,
|
||
这些节点的消息组处理器就会协同作出决定该如何对这个消息进行路由。</para>
|
||
<para>消息组处理器有两种:本地消息组处理器和远程消息组处理器。在一个集群中要选择一个节点作为本地消息组处理器的
|
||
节点,集群中所有其它的节点都持有远程消息组处理器。在集群中由本地消息组处理器最終决定消息怎样路由,其它的远程
|
||
处理器配合本地处理器完成决策。消息组处理器的配置在<emphasis role="italic">activemq-configuration.xml</emphasis>
|
||
文件中,下面就是一个例子:
|
||
<programlisting> <grouping-handler name="my-grouping-handler">
|
||
<type>LOCAL</type>
|
||
<address>jms</address>
|
||
<timeout>5000</timeout>
|
||
</grouping-handler>
|
||
|
||
<grouping-handler name="my-grouping-handler">
|
||
<type>REMOTE</type>
|
||
<address>jms</address>
|
||
<timeout>5000</timeout>
|
||
</grouping-handler></programlisting></para>
|
||
<para><emphasis role="italic">address</emphasis>属性表示一个集群的连接以及它使用的地址。有关如何配置集群
|
||
参见集群章节。<emphasis role="italic">timeout</emphasis>属性代表做出路由决定所需要等待的时间。如果超过
|
||
了这个时间还没有做出决定,则会抛出异常。这可以保证严格的顺序。</para>
|
||
<para>收到消息的节点会首先提出一个消息路由的建议。它采用轮询方式来选择一个合适的路由。它首先选择一个本地的队列,之后
|
||
再选择一个有接收者的队列。如果这个建议被所有组处理器接受,消息就会被路由到所选的队列。如果被拒绝就提出另一个路
|
||
由方案,如此反复直到方案被接受为止。队列选择后所有其它的节点都将消息路由到这个队列。这样所有的消息组的消息在一个
|
||
节点上进行处理,也就是该节点上的接收者接收所有的同组的消息。</para>
|
||
<para>由于只有一个本地处理器,如果它的节点出现故障则无法做出决定。这时所有的消息将不能被传递,并且会抛出异常。
|
||
为了避免这一单点故障,本地处理器可以在备份节点上有一个复本。只要创建备份节点并配置一个相同的本地处理器即可。</para>
|
||
<para/>
|
||
<section>
|
||
<title>集群消息组的最佳使用惯例</title>
|
||
<para>下面是一些很好的建议:<orderedlist>
|
||
<listitem>
|
||
<para>尽可能使接收者均匀分布在不同的节点上。由于消息组的消息总是传递到同一个队列的同一个接收者,
|
||
如果你经常性地创建与关闭接收者,就可能出现消息由于没有接收者而传递不出去,造成消息在队列中
|
||
不断积累的情况。因此,尽量要避免关闭接收者,或者确保有足够数量的接收者。例如,如果你的集群
|
||
有3个节点,那么就创建3个接收者。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>尽可能使用持久型队列。如果消息组的消息与一个队列绑定,一旦这个队列被删除,其它节点可能仍然
|
||
尝试向这个已删除的队列路由消息。为避免这种情况,要确保这个队列由发送消息的会话来删除。这样如果
|
||
下一个消息发出后发现原来的队列被删除,新的路由建议就会提出。另外一种方案是你可以重新使用一个不
|
||
同的组ID。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>一定要确保本地的消息组处理器有备份。这样在有故障时消息组仍然可以正常工作。</para>
|
||
</listitem>
|
||
</orderedlist></para>
|
||
</section>
|
||
<section>
|
||
<title>集群消息组例子</title>
|
||
<para>参见<xref linkend="examples.clustered.grouping"/>,这个例子给出了如何在ActiveMQ集群中配置消息组。</para>
|
||
</section>
|
||
</section>
|
||
</chapter>
|