【问题标题】:The number of messages on an MSMQ via Powershell通过 Powershell 在 MSMQ 上的消息数
【发布时间】:2011-01-18 08:51:14
【问题描述】:

我想提供一个队列路径并获取消息的数量。关于如何做到这一点的任何建议?

【问题讨论】:

    标签: powershell msmq


    【解决方案1】:

    PowerShell Community Extensions 中有一组 MSMQ 管理 cmdlet。试试这些,看看它们是否有帮助(可能是 Get-MSMQueue):

    Clear-MSMQueue
    Get-MSMQueue
    New-MSMQueue
    Receive-MSMQueue
    Send-MSMQueue
    Test-MSMQueue
    

    注意:尝试获取基于 beta 2.0 模块的发行版 - 只需记住在解压缩之前“解封” zip。

    【讨论】:

    • PCX 要求已安装?我只想在远程服务器中执行 XCOPY 部署
    • 更高版本的 PowerShell 附带 MSMQ 模块 - 虽然我不知道该模块是否仅在 Win8/Server2012 上可用。
    【解决方案2】:

    所以,我看到了这个:What can I do with C# and Powershell?,然后去了这里:http://jopinblog.wordpress.com/2008/03/12/counting-messages-in-an-msmq-messagequeue-from-c/

    做了这个

    # Add the .NET assembly MSMQ to the environment.
    [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
    
    # Create a new QueueSizer .NET class help to warp MSMQ calls.
    $qsource = @"
    public class QueueSizer
        {
            public static System.Messaging.Message PeekWithoutTimeout(System.Messaging.MessageQueue q, System.Messaging.Cursor cursor, System.Messaging.PeekAction action)
            {
                System.Messaging.Message ret = null;
                try
                {
                    // Peek at the queue, but timeout in one clock tick.
                    ret = q.Peek(new System.TimeSpan(1), cursor, action);
                }
                catch (System.Messaging.MessageQueueException mqe)
                {
                    // Trap MSMQ exceptions but only ones relating to timeout. Bubble up any other MSMQ exceptions.
                    if (!mqe.Message.ToLower().Contains("timeout"))
                    {
                        throw;
                    }
                }
                return ret;
            }
    
            // Main message counting method.
            public static int GetMessageCount(string queuepath)
            {
                // Get a specific MSMQ queue by name.
                System.Messaging.MessageQueue q = new System.Messaging.MessageQueue(queuepath);
    
                int count = 0;
    
                // Create a cursor to store the current position in the queue.
                System.Messaging.Cursor cursor = q.CreateCursor();
    
                // Have quick peak at the queue.
                System.Messaging.Message m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Current);
    
                if (m != null)
                {
                    count = 1;
    
                    // Keep on iterating through the queue and keep count of the number of messages that are found.
                    while ((m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Next)) != null)
                    {
                        count++;
                    }
                }
    
                // Return the tally.
                return count;
            }
        }
    "@
    
    # Add the new QueueSizer class helper to the environment.
    Add-Type -TypeDefinition $qsource -ReferencedAssemblies C:\Windows\assembly\GAC_MSIL\System.Messaging\2.0.0.0__b03f5f7f11d50a3a\System.Messaging.dll
    
    # Call the helper and get the message count.
    [QueueSizer]::GetMessageCount('mymachine\private$\myqueue');
    

    它奏效了。

    【讨论】:

    • +1 好答案。另外,不知道为什么,但我必须在最后一行将“QueueSizer::”更改为“[QueueSizer]::”,才能使该脚本正常工作。
    • @JohnD +1 方括号表示类型。正如你提到的,是必需的。因为 GetMessageCount 是静态方法,所以需要直接引用类型。如果它是一个实例方法,则必须首先创建一个对象,例如 $qs = New-Object QueueSizer;然后参考 $qs.NetMessageCourt...
    • 这是一个糟糕的答案,因为它正在遍历消息并对其进行计数。将 1,000,000 条消息添加到您的队列并运行它,您会看到需要多长时间
    【解决方案3】:

    Irwin 提供的解决方案并不理想。

    您可以拨打.GetAllMessages 来一次检查完成此操作,而不是使用foreach 循环。

    $QueueName = "MycomputerName\MyQueueName" 
    $QueuesFromDotNet =  new-object System.Messaging.MessageQueue $QueueName
    
    
    If($QueuesFromDotNet.GetAllMessages().Length -gt $Curr)
    {
        //Do Something
    }
    

    .Length 为您提供给定队列中的消息数。

    【讨论】:

    • 但是这样安全吗?这两种解决方案都不会潜在地阻止读取这些队列的其他进程吗?
    • 当你的队列有上万条消息时,这种方法的性能很糟糕,不是一个好的选择。
    • 这里也一样。这个脚本需要千兆字节的内存来处理大消息和长队列
    • 花点时间给最后两个 cmets 投票(我刚刚投了)。这是一个公平的批评。对你们两个来说,哪个选项对你们有用?你投了那个选项吗?你有更好的选择吗?我已经有一段时间没有做过 MSMQ 工作了,希望以后不要再做 :-)
    • "这是获取消息计数的低效方法,因为它需要读取所有消息并将它们存储在内存中的数组中。这可能会导致巨大的内存消耗,具体取决于频率、大小消息和队列中的当前消息数量,并可能影响应用程序的性能。”更好地使用 WMI:dejanstojanovic.net/aspnet/2018/january/…
    【解决方案4】:

    这将列出机器上的所有队列和消息数量:

    gwmi -class Win32_PerfRawData_MSMQ_MSMQQueue -computerName $computerName |
        ft -prop Name, MessagesInQueue
    

    【讨论】:

    • 实际上,我注意到 WMI 调用并不总是枚举所有队列。不知道为什么。我最终在服务器列表上调用 [System.Messaging.MessageQueue]::GetPublicQueuesByMachine($machineName),然后使用 Irwin 的 [QueueSizer]::GetMessageCount($queuepath) 方法获取每个队列的消息计数。
    • 有什么办法可以得到超过前 100 条消息?
    • 不幸的是,这会将队列名称截断为 64 个字符,并且只返回前 97 个结果。 :(
    • @RobO 有没有办法获取完整脚本的副本?谢谢。
    【解决方案5】:

    Windows Server 2012/2012 R2 和 Windows 8/8.1 下的 PowerShell 具有大量内置 Cmdlet,可通过安装 Microsoft 消息队列 (MSMQ) Server Core 功能使用。

    # Get all message queues
    Get-MsmqQueue;
    
    # Get all the private message queues.
    # Display only the QueueName and MessageCount for each queue.
    Get-MsmqQueue -QueueType Private | Format-Table -Property QueueName,MessageCount;
    

    还有许多其他 Cmdlet 可用于队列管理和消息创建。即

    • 新建-MsmqQueue
    • 删除-MsmqQueue
    • 发送-MsmqQueue
    • 接收-MsmqQueue
    • 获取 MsmqQueueManager

    有关 MSMQ Cmdlet 帮助的完整列表,请参阅 MSMQ Cmdlets in Windows PowerShell,如果您已经安装了该功能,请参阅 Get-Command -Module MSMQ

    【讨论】:

    【解决方案6】:

    按照this link的规定,你可以使用

    $queues = Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue
    $queues | ft -property Name,MessagesInQueue
    

    获取本地队列的大小,或

    $host = ...
    $cred = get-credential
    $queues = Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue -computer $host -credential $cred
    $queues | ft -property Name,MessagesInQueue
    

    用于远程队列。

    【讨论】:

    • 这在没有任何新 cmdlet 的旧 2008 R2 服务器上完美运行
    【解决方案7】:

    尝试其中一个...

    function GetMessageCount2($queuename)
    {
        $queuename = $env:computername + "\" + $queuename
        return (Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue | Where-Object -filterscript {$_.Name -eq $queuename}).MessagesinQueue 
    }
    
    function GetMessageCount3($queuename)
    {
        return (Get-MsmqQueue | Where-Object -FilterScript {$_.QueueName -eq $queuename}).MessageCount
    }
    

    【讨论】:

      【解决方案8】:

      我一直在寻找一个好的答案,虽然 Irwin 的回答让我处于正确的基础上,但我正在寻找一些更像 Powershell 的代码。这样做的主要原因是为了处理更改,因为在 .Net 运行时中加载了类型,因此您不能多次 Add-Type,并且在不关闭您的 powershell 实例的情况下无法卸载。

      所以我接受了他的回答并想出了:

      # Add the .NET assembly MSMQ to the environment.
      [Reflection.Assembly]::LoadWithPartialName("System.Messaging") | out-Null
      
      function Get-QueueNames([Parameter(Mandatory=$true)][String]$machineName, [String]$servicePrefix)
      {
          [System.Messaging.MessageQueue]::GetPrivateQueuesByMachine($machineName) |
              ForEach-Object { $_.Path } | 
              Where-Object { $_ -like "*$($servicePrefix).*" } 
      }
      
      function Get-MessageCount([parameter(Mandatory=$true)][String]$queueName)
      {
          function HasMessage
          {
              param
              (
                  [System.Messaging.MessageQueue]$queue,
                  [System.Messaging.Cursor]$cursor,
                  [System.Messaging.PeekAction]$action
              )
      
              $hasMessage = $false
              try
              {
                  $timeout = New-Object System.TimeSpan -ArgumentList 1
                  $message = $queue.Peek($timeout, $cursor, $action)
                  if ($message -ne $null)
                  {
                      $hasMessage = $true
                  }
              }
              catch [System.Messaging.MessageQueueException]
              {
                  # Only trap timeout related exceptions
                  if ($_.Exception.Message -notmatch "timeout")
                  {
                      throw
                  }
              }
      
              $hasMessage
          }
      
          $count = 0
          $queue = New-Object System.Messaging.MessageQueue -ArgumentList $queueName
          $cursor = $queue.CreateCursor()
      
          $action = [System.Messaging.PeekAction]::Current
          $hasMessage = HasMessage $queue $cursor $action
          while ($hasMessage)
          {
              $count++
              $action = [System.Messaging.PeekAction]::Next
              $hasMessage = HasMessage $queue $cursor $action
          }
      
          $count
      }
      
      $machineName = "."
      $prefix = "something"
      
      Get-QueueNames $machineName $prefix |
          ForEach-Object {
              New-Object PSObject -Property @{
                  QueueName = $_
                  MessageCount = Get-MessageCount $_
              }
          }
      

      可以进行优化,使第一个函数返回队列而不是队列名称,但我需要两者用于不同的场景。

      【讨论】:

        【解决方案9】:
        winrm s winrm/config/client '@{TrustedHosts="yourIp"}'
        $securePassword = ConvertTo-SecureString "YourPassword" -AsPlainText -force
        $credential = New-Object System.Management.Automation.PsCredential("Domain\Usernama",$securePassword)  
        $session = New-PSSession YourIP -credential $credential
        $command = {Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue | ft -property Name,MessagesinJournalQueue,MessagesInQueue | out-String} 
        Invoke-Command -session $session -scriptblock $command
        

        【讨论】:

        • 一些 cmets 在这里会很有用。
        【解决方案10】:

        将此用于您的 c# 块以获取计数。它使用性能计数器单次查询:

        public static int GetMessageCount(string machineName, string queuepath)
            {
                var queueCounter = new PerformanceCounter(
                    "MSMQ Queue",
                    "Messages in Queue",
                    string.Format("{0}\\{1}", machineName, queuepath),
                    machineName);
        
                return (int)queueCounter.NextValue();
            }
        

        这比重复查看更有效,因为工作主要在远程机器上完成,也比 GetAllMessages 更有效,因为它返回额外的消息数据,然后计算元素 - 在任何实际负载下的性能都很糟糕。

        【讨论】:

          【解决方案11】:

          这对我有用

          [System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
          
          $AlertCount = 39
          
          $queuePath = ".\private$\test.pdpvts.error"
          
          $queue = New-Object System.Messaging.MessageQueue $queuePath
          
          
          If($queue.GetAllMessages().Length -gt $AlertCount)
          {
              Send-MailMessage -To "Me" -From "Alerts" -Subject "Message queue is full" -Credential mycridentials -UseSsl -SmtpServer mail.google.com
          }
          

          【讨论】:

            【解决方案12】:

            我一直在寻找有关访问集群中队列的信息。

            对于其他尝试在集群队列上使用 powershell 命令的人:

            在集群节点之一上:

            $env:computername = "MsmqHostName"
            Get-MsmqQueue | Format-Table -Property QueueName,MessageCount
            

            远离集群:

            Invoke-Command -ScriptBlock {$env:computername = "msmqHostName";Get-MsmqQueue | Format-Table -Property QueueName,MessageCount } -ComputerName ClusternNodeName
            

            【讨论】:

              猜你喜欢
              • 2012-08-26
              • 1970-01-01
              • 1970-01-01
              • 2014-05-24
              • 1970-01-01
              • 2015-03-11
              • 1970-01-01
              • 2017-04-27
              • 2010-09-17
              相关资源
              最近更新 更多