121 lines
8.6 KiB
XML
121 lines
8.6 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="duplicate-detection">
|
||
<title>重复消息检测</title>
|
||
<para>ActiveMQ具有强大的自动检测重复消息的功能。应用层无需实现复杂的重复检测。本章解释了什么是重复检测,它
|
||
在ActiveMQ中如何工作的,以及如何进行配置。</para>
|
||
<para>当客户端向服务器端发送消息时,或者从一个服务器向另一个服务器传递消息时,如果消息发送后目标服务器或者
|
||
连接出现故障,导致发送一方没有收到发送成功的确认信息,发送方因此就无法确定消息是否已经成功发送到了目标地
|
||
址。</para>
|
||
<para>如果上述的故障发生在消息被成功接收并处理后,但是在向发送方返回功能确认信息之前,那么消息实际上可以到达
|
||
其目的地址;如果故障发生在消息的接收及处理过程中,则消息不会到达其目的地址。从发送方的角度看,它无法区分
|
||
这两种情况。</para>
|
||
<para>当服务器恢复后,客户端面临的困难的选择。它知道服务器出了故障,但是不知道刚刚发送的消息是否成功到达目的
|
||
地址。如果它重新发送这个消息,就有可能造成消息的重复。如果这个消息是一个订单的话,重复发送消息就会产生两
|
||
个相同的订单,这当然不是所希望的結果。</para>
|
||
<para>将消息的发送放到一个事务中也不能解决这个问题。如果在事务提交的过程中发生故障,同样不能确定这个事务是否提交
|
||
成功!</para>
|
||
<para>为了解决这个问题,ActiveMQ提供了自动消息重复检测功能。</para>
|
||
<section>
|
||
<title>在消息发送中应用重复检测</title>
|
||
<para>在消息发送中启用重复检测功能十分简单:你只需将消息的一个特殊属性设置一个唯一值。你可以用任意方法来
|
||
计算这个值,但是要保证它的唯一性。当目标服务器接收到这个消息时,它会检查这个属性是否被设置,如果设置了,
|
||
就检查内存缓存中是否已经接收到了相同值的消息。如果发现已经接收过具有相同属性值的消息,它将忽略这个消息。</para>
|
||
<note>
|
||
<para>在节点之间的消息传递使用重复消息检测可以保证<emphasis>一次且只一次</emphasis>的传递,和使用
|
||
XA事务接收消息的效果一样,但是比XA消耗的资源要少,并且更容易。</para>
|
||
</note>
|
||
<para>如果是在一个事务中发送消息,则只需要设置其中一个消息的属性值。在服务器端如果服务器检测到一个事务中某一个
|
||
消息重复,则会忽略整个事务。</para>
|
||
<para>这个属性的名称由<literal
|
||
>org.apache.activemq.api.core.HDR_DUPLICATE_DETECTION_ID</literal>定义,即:
|
||
<literal>_HQ_DUPL_ID</literal>。</para>
|
||
<para>该属性的值可以是<literal>byte[]</literal>类型或<literal
|
||
>SimpleString</literal>类型(核心接口)。如果使用JMS,它必须是<literal>String</literal>
|
||
类型。它的值一定是唯一的。一个简单的方法是使用UUID。</para>
|
||
<para>下面是一个使用核心接口设置这个属性的例子:</para>
|
||
<programlisting>
|
||
...
|
||
|
||
ClientMessage message = session.createMessage(true);
|
||
|
||
SimpleString myUniqueID = "This is my unique id"; // Could use a UUID for this
|
||
|
||
message.setStringProperty(HDR_DUPLICATE_DETECTION_ID, myUniqueID);
|
||
|
||
...
|
||
</programlisting>
|
||
<para>下面则是一个使用JMS的例子:</para>
|
||
<programlisting>
|
||
...
|
||
|
||
Message jmsMessage = session.createMessage();
|
||
|
||
String myUniqueID = "This is my unique id"; // Could use a UUID for this
|
||
|
||
message.setStringProperty(HDR_DUPLICATE_DETECTION_ID.toString(), myUniqueID);
|
||
|
||
...
|
||
</programlisting>
|
||
</section>
|
||
<section id="duplicate.id.cache">
|
||
<title>配置重复ID缓存</title>
|
||
<para>服务器缓存中保存接收到的消息的<literal
|
||
>org.apache.activemq.core.message.impl.HDR_DUPLICATE_DETECTION_ID</literal>属性值。每个地址有
|
||
单独的缓存。</para>
|
||
<para>缓存的大小是固定的,循环使用。如果缓存的最大可以存放<literal
|
||
>n</literal>条记录,那么<literal>n + 1</literal>条记录将会覆盖缓存中的第<literal>0</literal>
|
||
条记录。</para>
|
||
<para>缓存的最大容量在文件<literal>activemq-configuration.xml</literal>中配置,参数是<literal
|
||
>id-cache-size</literal>。默认值是<literal>2000</literal>条记录。</para>
|
||
<para>在文件<literal>activemq-configuration.xml</literal>中还可以配置将缓存持久化到磁盘。相应的参数
|
||
是<literal>persist-id-cache</literal>。如果设为<literal>true</literal>,则每加入一个id就将
|
||
它同时保存到磁盘中。默认值是<literal>true</literal>。</para>
|
||
<note>
|
||
<para>注意在设置缓存大小时,一定要保证缓存能保存足夠数量的记录,当消息被重新发送时,之前发送的ID不被
|
||
覆盖掉。</para>
|
||
</note>
|
||
</section>
|
||
<section>
|
||
<title>桥与重复检测</title>
|
||
<para>核心桥可以通过配置在将消息发向目的服务器之前自动向消息中添加唯一的id(如果消息中还没有的话)。这样
|
||
如果目的服务器发生故障,核心桥在重新发送消息时,目的服务器就可以自动检测重复的消息,发现重复消息即丢弃。</para>
|
||
<para>要配置核心桥的自动添加id的功能,需要在<literal>activemq-configuration.xml</literal>中桥的配置
|
||
里将<parameter>use-duplicate-detection</parameter>参数设为<literal>true</literal>。</para>
|
||
<para>这个参数的默认值是<literal>true</literal>。</para>
|
||
<para>关于核心桥的配置和使用,参见<xref linkend="core-bridges" />。</para>
|
||
</section>
|
||
<section>
|
||
<title>重复检测与集群连接</title>
|
||
<para>集群连接内部使用核心桥在节点间可靠地移动消息,因此它们的核心桥也可以配置自动添加id的功能。</para>
|
||
<para>配置的方法是在<literal>activemq-configuration.xml</literal>文件中将集群连接的
|
||
<parameter>use-duplicate-detection</parameter>参数设为<literal>true</literal>。</para>
|
||
<para>这个参数的默认值是<literal>true</literal>。</para>
|
||
<para>有关集群连接配置的更多信息,请参见<xref linkend="clusters"/>。</para>
|
||
</section>
|
||
<section>
|
||
<title>分页转存与重复检测</title>
|
||
<para>ActiveMQ在将消息进行分页转存中也使用了重复检测。当分页转存消息被从磁盘中读回到内存时,如果服务器发生故障,
|
||
重复检测可以避免在这一过程中有消息被重复读入,即避免了消息的重复传递。</para>
|
||
<para>关于分页转存的配置信息请参见<xref linkend="paging" />。</para>
|
||
</section>
|
||
</chapter>
|