【问题标题】:Is there a way to check how many messages are in a MSMQ Queue?有没有办法检查 MSMQ 队列中有多少消息?
【发布时间】:2011-04-21 14:33:40
【问题描述】:

我想知道是否有一种方法可以使用 C# 以编程方式检查私有或公共 MSMQ 中有多少消息?我有代码可以检查队列是否为空或不使用包装在 try/catch 中的 peek 方法,但我从未见过任何有关显示队列中消息数量的信息。这对于监控队列是否正在备份非常有帮助。

【问题讨论】:

    标签: c# msmq


    【解决方案1】:

    如果您想要一个私有队列的计数,您可以使用 WMI 执行此操作。 这是这个的代码:

    // You can change this query to a more specific queue name or to get all queues
    private const string WmiQuery = @"SELECT Name,MessagesinQueue FROM Win32_PerfRawdata_MSMQ_MSMQQueue WHERE Name LIKE 'private%myqueue'";
    
    public int GetCount()
    {
        using (ManagementObjectSearcher wmiSearch = new ManagementObjectSearcher(WmiQuery))
        {
            ManagementObjectCollection wmiCollection = wmiSearch.Get();
    
            foreach (ManagementBaseObject wmiObject in wmiCollection)
            {
                foreach (PropertyData wmiProperty in wmiObject.Properties)
                {
                    if (wmiProperty.Name.Equals("MessagesinQueue", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return int.Parse(wmiProperty.Value.ToString());
                    }
                }
            }
        }
    }
    

    感谢Microsoft.Windows.Compatibility 包,这也适用于netcore/netstandard。

    【讨论】:

      【解决方案2】:

      由于xxx does not exist in the specified Category 错误,我很难让接受的答案正常工作。上述解决方案都不适合我。

      但是,简单地指定机器名称如下似乎可以解决它。

      private long GetQueueCount()
      {
          try
          {
              var queueCounter = new PerformanceCounter("MSMQ Queue", "Messages in Queue", @"machineName\private$\stream")
              {
                  MachineName = "machineName"
              };
      
              return (long)queueCounter.NextValue();
          }
          catch (Exception e)
          {
              return 0;
          }
      }
      

      【讨论】:

        【解决方案3】:

        队列中的消息计数可以使用以下代码找到。

        MessageQueue messageQueue = new MessageQueue(".\\private$\\TestQueue");
        var noOFMessages = messageQueue.GetAllMessages().LongCount();
        

        【讨论】:

        • 在这种情况下,您会从队列中获取所有消息。太贵了。
        【解决方案4】:

        如果你需要一个快速的方法(我的机器每秒调用 25k),我推荐 Ayende 的基于 MQMgmtGetInfo() 和 PROPID_MGMT_QUEUE_MESSAGE_COUNT 的版本:

        对于 C# https://github.com/hibernating-rhinos/rhino-esb/blob/master/Rhino.ServiceBus/Msmq/MsmqExtensions.cs

        对于 VB https://gist.github.com/Lercher/5e1af6a2ba193b38be29

        起源可能是http://functionalflow.co.uk/blog/2008/08/27/counting-the-number-of-messages-in-a-message-queue-in/,但我不相信这个从 2008 年开始的实现是否可以继续工作。

        【讨论】:

        • 不要寻找其他答案!除了@Zartag 的回答,任何其他方法要么缓慢要么不可靠(我已经测试了所有这些方法,相信我!)
        • nuget 包Rsft.Lib.Msmq.MessageCounter 实现了这一点。
        • 慢是相对的@meraydin 但是是的,这绝对是最快的。
        • 也是需要最多(复杂)代码的一种
        • 不幸的是,@JeroenMaes 在调用库的 GetCount 方法时遇到异常。在他们的存储库中提出了问题。
        【解决方案5】:

        没有可用的 API,但您可以使用足够快的 GetMessageEnumerator2。示例:

        MessageQueue q = new MessageQueue(...);
        int count = q.Count();
        

        实施

        public static class MsmqEx
        {
            public static int Count(this MessageQueue queue)
            {
                int count = 0;
                var enumerator = queue.GetMessageEnumerator2();
                while (enumerator.MoveNext())
                    count++;
        
                return count;
            }
        }
        

        我也尝试了其他选项,但每个选项都有一些缺点

        1. 性能计数器可能抛出异常“指定类别中不存在实例'...'。”
        2. 读取所有消息然后计数真的很慢,它还会从队列中删除消息
        3. Peek 方法似乎有问题,会引发异常

        【讨论】:

        • 这里没有竞争条件吗?如果在枚举时添加/删除内容会发生什么?或者,如果在您计数后添加/删除了东西?有没有办法在计数时暂停与队列的所有交互?
        • 这个方法在某种意义上似乎是线程安全的,它反映了队列的动态变化。来自doc“例如,枚举器可以自动访问位于光标当前位置之外的低优先级消息,但不能访问插入在该位置之前的高优先级消息。”。就个人而言,我在插入和删除期间在负载下对其进行了测试,并且没有线程问题。显然,这将反映大约。如果使用您的队列,则消息计数。如果需要准确计数,则需要通过调用GetAllMessages获取静态快照
        【解决方案6】:

        这对我有用。首先使用枚举器确保队列为空。

           Dim qMsg As Message ' instance of the message to be picked 
                Dim privateQ As New MessageQueue(svrName & "\Private$\" & svrQName) 'variable svrnme = server name ; svrQName = Server Queue Name
                privateQ.Formatter = New XmlMessageFormatter(New Type() {GetType(String)}) 'Formating the message to be readable the body tyep
                Dim t As MessageEnumerator 'declared a enumarater to enable to count the queue
                t = privateQ.GetMessageEnumerator2() 'counts the queues 
        
                If t.MoveNext() = True Then 'check whether the queue is empty before reading message. otherwise it will wait forever 
                    qMsg = privateQ.Receive
                    Return qMsg.Body.ToString
                End If
        

        【讨论】:

        • 这是一个C#问题,你的答案是VB
        【解决方案7】:
                    //here queue is msmq queue which you have to find count.        
                    int index = 0;
                    MSMQManagement msmq = new MSMQManagement() ;   
                    object machine = queue.MachineName;
                    object path = null;
                    object formate=queue.FormatName;
                    msmq.Init(ref machine, ref path,ref formate);
                    long count = msmq.MessageCount();
        

        这比您选择的要快。 您可以在“C:\Program Files (x86)\Microsoft SDKs\Windows”中获得 MSMQManagement 类引用,只需浏览此地址即可。更多详情,您可以访问http://msdn.microsoft.com/en-us/library/ms711378%28VS.85%29.aspx。

        【讨论】:

        • Windows 文件夹到底在哪里?有 4 个直接子文件夹,每个子文件夹都有更多子文件夹...我必须搜索什么文件?
        【解决方案8】:

        您可以直接从 .NET 中读取队列的性能计数器值:

        using System.Diagnostics;
        
        // ...
        var queueCounter = new PerformanceCounter(
            "MSMQ Queue", 
            "Messages in Queue", 
            @"machinename\private$\testqueue2");
        
        Console.WriteLine( "Queue contains {0} messages", 
            queueCounter.NextValue().ToString());
        

        【讨论】:

        • 适用于一个队列,但对于错误队列,我得到“指定类别中不存在实例 'machinename\private$\error'。”。那是因为队列有一段时间没有队列中的任何项目还是我需要以特殊方式写入错误队列?
        • 我在错误队列中添加了一条消息,然后代码也适用于该队列。我猜你需要捕获这个异常(InvalidOperationException)然后它是空的。不好的是,如果遇到此异常,队列也可能不存在。
        • 我发现像“.\private$\testqueue2”这样我从 someQueue.Path 得到的语法会导致异常。我最终使用了 someQueue.FormatName 并从其内容中提取了“machinename\private$\testqueue2”。我还发现,当队列刚刚启动或检索时,这种方法并不是 100% 可靠的,这让我认为使用基于 com dll 的实现会更好,但我没有时间投入更多测试.
        • 我收到“类别不存在。”尝试实例化 PerformanceCounter() 时。队列中必须有 > 0 条消息吗?克里斯蒂安,你找到其他工作了吗?在我的情况下,我不能保证消息在队列中。
        • 这是迄今为止我测试过的最快的方法
        【解决方案9】:

        我发现检索消息队列计数的最快方法是使用以下site 中的 peek 方法:

        protected Message PeekWithoutTimeout(MessageQueue q, Cursor cursor, PeekAction action)
        {
          Message ret = null;
          try
          {
             ret = q.Peek(new TimeSpan(1), cursor, action);
          }
          catch (MessageQueueException mqe)
          {
             if (!mqe.Message.ToLower().Contains("timeout"))
             {
                throw;
             }
          }
          return ret;
        }
        
        protected int GetMessageCount(MessageQueue q)
        {
          int count = 0;
          Cursor cursor = q.CreateCursor();
        
          Message m = PeekWithoutTimeout(q, cursor, PeekAction.Current);
          {
             count = 1;
             while ((m = PeekWithoutTimeout(q, cursor, PeekAction.Next)) != null)
             {
                count++;
             }
          }
        return count;
        }
        

        【讨论】:

        • System.Messaging.MessageQueueException:指定给 MQReceiveMessage 的 MQ_ACTION_PEEK_NEXT 不能与当前光标位置一起使用。在 System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 action, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType) at System.Messaging.MessageQueue.Peek(TimeSpan timeout, Cursor cursor, PeekAction action)
        • 我们正在使用这个确切的方法,并且发现方法 GetMessageCount 失败了,因为它在从队列中消费消息时偷看。通过在 count++ 之后仅添加一个 10 毫秒的 thread.sleep 命令,这运行起来更加顺畅,但需要更长的时间。我仍然认为必须有更好的方法来计数,坚持一百万次偷看......(我们到了吗,我们到了,我们到了 - doh)
        • 如果队列包含 0 条消息,此代码将失败,因为即使 PeekWithTimeout 和 PeekAction.Current 返回 null,它也会尝试 PeekWithoutTimeout 和 PeekAction.Next。添加空检查以回答
        • 这绝对不是最快的方法。投票率最高的答案:快 6 倍。投票第二高的答案:快 3 倍
        【解决方案10】:

        我们使用 MSMQ 互操作。根据您的需要,您可以简化此操作:

            public int? CountQueue(MessageQueue queue, bool isPrivate)
            {
                int? Result = null;
                try
                {
                    //MSMQ.MSMQManagement mgmt = new MSMQ.MSMQManagement();
                    var mgmt = new MSMQ.MSMQManagementClass();
                    try
                    {
                        String host = queue.MachineName;
                        Object hostObject = (Object)host;
                        String pathName = (isPrivate) ? queue.FormatName : null;
                        Object pathNameObject = (Object)pathName;
                        String formatName = (isPrivate) ? null : queue.Path;
                        Object formatNameObject = (Object)formatName;
                        mgmt.Init(ref hostObject, ref formatNameObject, ref pathNameObject);
                        Result = mgmt.MessageCount;
                    }
                    finally
                    {
                        mgmt = null;
                    }
                }
                catch (Exception exc)
                {
                    if (!exc.Message.Equals("Exception from HRESULT: 0xC00E0004", StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (log.IsErrorEnabled) { log.Error("Error in CountQueue(). Queue was [" + queue.MachineName + "\\" + queue.QueueName + "]", exc); }
                    }
                    Result = null;
                }
                return Result;
        
            }
        

        【讨论】:

        • 谢谢,但是 MSMQ.ManagementClass 在哪里?我在另一篇文章中读到,您必须包含一个名为“MSMQ 3.0”的 COM 库,但我在 Com 选项卡上没有看到这个(使用 .NET 3.5。)
        • 您的机器上是否有 Interop.MSMQ.dll?我们的程序集集合中有它。
        • 不,我没有,也没有看到任何地方可以在线下载。
        • 查看这个答案以了解如何添加此参考:stackoverflow.com/a/8258952/16241
        • 尝试了这种方法并得到了以下 COMException,不幸的是我无法解决,因为这需要更改基础结构:在工作组模式下安装的消息队列不支持此操作。 :(
        猜你喜欢
        • 1970-01-01
        • 2011-02-06
        • 2013-09-25
        • 2014-03-07
        • 2011-07-12
        • 2017-12-02
        • 2013-06-07
        • 1970-01-01
        相关资源
        最近更新 更多