.net-MQ系列消息被读取多次

我遇到一个问题,即偶尔会从Q中多次读取一条消息.我使用MQSeries的.NET包装器(amqmdnet.dll),并使用Win Service读取消息.

Here's how I do it with VB.NET:

'QManager
 Dim properties As Hashtable = New Hashtable(4)

        properties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_CLIENT)
        properties.Add(MQC.CHANNEL_PROPERTY, channelName)
        properties.Add(MQC.HOST_NAME_PROPERTY, iPAddress)
        properties.Add(MQC.PORT_PROPERTY, port)

        QManager = New MQQueueManager(queueManagerName, _
                                              properties)


'Q itself
getMessageOptions.Options = _
            MQC.MQGMO_FAIL_IF_QUIESCING Or _
            MQC.MQGMO_WAIT Or _
            MQC.MQGMO_SYNCPOINT

getMessageOptions.WaitInterval = 1000 ' read from config

Dim locker As New Object

System.Threading.Monitor.Enter(locker)

Q.Get(message, getMessageOptions)

QManager.Commit()

System.Threading.Monitor.Exit(locker)

我听说Q.Get(message,getMessageOptions)使该消息对其他读者不可用,QManager.Commit只是从Q中删除了该消息(类似于.NET Peek& Dequeue).单独使用它应该消除对Monitor的需求.

在我的Win Service中,多个线程读取了Q,我们怀疑由于轮询间隔较短(100毫秒或更短),包装器没有足够的时间来更新消息的“读取”标志,因此它多次被拾起通过重叠的线程.从历史上看,同一条消息最多可以读取四次.

在增加轮询间隔之前,我想确定自己是否以正确的方式进行操作.有人能建议我采取任何办法吗?

谢谢.

最佳答案
让我们讨论一下帖子和后续评论中的一些要点:

>在WMQ中执行GET确实会使该消息对其他阅读器不可用.同步点之外的GET立即使消息永久出队,而同步点下的GET则使该消息对其他阅读器不可用,除非发生BACKOUT.但是,浏览操作不提供这种保护.
>队列管理器调解对消息的访问. MQ应用程序没有对队列或消息内部的直接操作,因此.Net包装程序没有机会操纵“读取标志”.
>等待间隔是如果队列中没有可用消息且值为1000,则未完成的GET阻塞程序的时间为一秒.如果队列中有很多消息,并且程序立即循环,则有效轮询间隔会很长,程序需要花费较长的时间来处理消息.由于队列管理器无需等待消息到达,因此等待时间间隔不会影响来自深队列的吞吐量的出队消息.
> WMQ提供了许多不同级别的可靠性.这些由诸如消息持久性,确认模式和事务性之类的东西控制.例如,在工作单元外部检索到的非持久消息最多提供一次可靠性.

关于线程和同步,WebSphere MQ:使用.Net手册has this to say

The implementation of WebSphere® MQ
.NET ensures that, for a given
connection (MQQueueManager object
instance), all access to the target
WebSphere MQ queue manager is
synchronized. The default behaviour is
that a thread that wants to issue a
call to a queue manager is blocked
until all other calls in progress for
that connection are complete. If you
require simultaneous access to the
same queue manager from multiple
threads within your program, create a
new MQQueueManager object for each
thread that requires concurrent
access. (This is equivalent to issuing
a separate MQCONN call for each
thread.)

If the default connection options are
overridden by
MQC.MQCNO_HANDLE_SHARE_NONE or
MQC.MQCNO_SHARE_NO_BLOCK then the
queue manager is no longer
synchronized.

因此,同步已为您完成,但确切的行为取决于每个线程是否具有自己的连接.这是因为事务是连接作用域的.如果一个连接服务多个线程,并且其中一个线程调用COMMIT,则该连接下的所有线程都在同一工作单元中,并且所有COMMIT都在同一时间.

除了浏览消息外,还有几种可能导致消息重复的可能性.所有这些都是要撤消的邮件的变体.退出可能并不总是在程序控制之下.当许多事务同时处于同步点下时,几个调整限制将起作用,包括MAXUMSGS,日志文件的主要和辅助范围以及其他几个.如果超过这些限制中的任何一个,QMgr将取消最旧的未完成工作单元,以便为下一个工作单元腾出空间.发生回退的另一个原因是客户端连接是否中断.在这种情况下,同步点下的所有GETS都会回滚,并且消息再次变得可用.

确定是否正在发生这种情况的一种方法是在读取消息时检查消息的回退计数.健壮的消息传递应用程序将定期检查此值,作为带毒消息处理的一部分,并在回退计数超过某个任意阈值的情况下重新排队(或删除和记录)消息.这样可以防止无法读取的消息永久锁定线程.当看到重复的消息时,记录回退计数超过零的所有消息而不是简单地重新排队超过阈值的消息可能会有所帮助.

确切了解正在发生的事情的另一种方法是使用tracing facilities of WMQSupportPac MA0W.这两种方法都将确切显示正在执行的API调用,由哪些应用程序和线程以及具有哪些选项.如果您的跟踪显示重复消息正在传递给非浏览GET调用而没有发生BACKOUT,则表明您可以请求修复此缺陷.

如果您使用的是较旧的.Net代码,则极有可能该问题是已修复的缺陷.我没有在修复列表中看到任何可能导致重复消息的缺陷,但是如果您的.Net客户端不是最新的v7,则无论如何应该考虑尽快升级. v7类的功能要强大得多,实际上,现在已经完全集成到WMQ中并得到支持.从2011年9月起,WMQ v6的支持也将停止支持.您可以在SupportPac MQC7中获得最新的WMQ客户端.如果您已经安装了v7客户端,并且需要升级到v7.0.1.4,则可以在较旧的v7上安装最新的客户端,或从Recommended Fixes page应用最新的修订包(在撰写本文时为FP 7.0.1.4).修订包将升级客户端以及服务器安装.

转载注明原文:.net-MQ系列消息被读取多次 - 代码日志