MQ系列消息被多次读取

3 .net ibm-mq

我有一个问题,即偶尔会从Q中读取一条消息.我正在使用.NET包装器为MQSeries(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)
Run Code Online (Sandbox Code Playgroud)

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

在我的Win Service中,多个线程读取Q并且我们怀疑由于轮询间隔较低(100ms或更短),包装器没有足够的时间来更新消息的"Read"标志,因此它被多次拾取通过重叠线程.历史上,相同的消息最多可读取四次.

在增加轮询间隔之前,我想确定我是否以正确的方式做事.任何人都可以用我的方法建议任何问题吗?

谢谢.

T.R*_*Rob 5

让我们从帖子和随后的评论中提出一些要点:

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

关于线程和同步,WebSphere MQ:Using .Net手册有这样的说法:

WebSphere®MQ.NET的实现确保对于给定连接(MQQueueManager对象实例),对目标WebSphere MQ队列管理器的所有访问都是同步的.默认行为是,在该连接的所有其他正在进行的调用完成之前,将阻止要向队列管理器发出调用的线程.如果需要从程序中的多个线程同时访问同一队列管理器,请为需要并发访问的每个线程创建一个新的MQQueueManager对象.(这相当于为每个线程发出单独的MQCONN调用.)

如果MQC.MQCNO_HANDLE_SHARE_NONE或MQC.MQCNO_SHARE_NO_BLOCK覆盖了缺省连接选项,则队列管理器不再同步.

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

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

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

另一种查看确切情况的方法是使用WMQSupportPac MA0W跟踪功能.这些中的任何一个都将准确显示正在执行的API调用,应用程序和线程以及哪些选项.如果您的跟踪显示重复邮件正在传送到非浏览GET调用而没有发生BACKOUT,那么您可以请求修复该缺陷.

如果您使用较旧的.Net代码,则问题是已修复的缺陷.我没有看到修复列表中的任何缺陷会导致重复的消息,但如果您的.Net客户端不在最近的v7,您应该考虑尽快升级.v7类功能更强大,实际上现在已完全集成到WMQ中并得到支持.截至2011年9月,还有一个小问题WMQ v6支持终止.您可以在SupportPac MQC7中获得最新的WMQ客户端.如果您已经安装了v7客户端并且需要升级到v7.0.1.4,则可以通过旧版本的v7安装最新客户端,或者从" 推荐修复"页面应用最新的修订包(撰写本文时为FP 7.0.1.4).Fix Pack升级客户端以及服务器安装.

  • 既然你正在使用同步点.工作单元是连接范围的. (2认同)