简体   繁体   English

我如何订阅MSMQ队列但只是“偷看”.Net中的消息?

[英]How do I subscribe to a MSMQ queue but only “peek” the message in .Net?

We have a MSMQ Queue setup that receives messages and is processed by an application. 我们有一个MSMQ队列设置,它接收消息并由应用程序处理。 We'd like to have another process subscribe to the Queue and just read the message and log it's contents. 我们想让另一个进程订阅Queue,只需阅读消息并记录它的内容。

I have this in place already, the problem is it's constantly peeking the queue. 我已经有了这个,问题是它一直在偷看队列。 CPU on the server when this is running is around 40%. 运行时服务器上的CPU大约为40%。 The mqsvc.exe runs at 30% and this app runs at 10%. mqsvc.exe以30%运行,此应用程序以10%运行。 I'd rather have something that just waits for a message to come in, get's notified of it, and then logs it without constantly polling the server. 我宁愿有一些东西只是等待消息进来,得到它的通知,然后记录它而不必经常轮询服务器。

    Dim lastid As String
    Dim objQueue As MessageQueue
    Dim strQueueName As String

    Public Sub Main()
        objQueue = New MessageQueue(strQueueName, QueueAccessMode.SendAndReceive)
        Dim propertyFilter As New MessagePropertyFilter
        propertyFilter.ArrivedTime = True
        propertyFilter.Body = True
        propertyFilter.Id = True
        propertyFilter.LookupId = True
        objQueue.MessageReadPropertyFilter = propertyFilter
        objQueue.Formatter = New ActiveXMessageFormatter
        AddHandler objQueue.PeekCompleted, AddressOf MessageFound

        objQueue.BeginPeek()
    end main

    Public Sub MessageFound(ByVal s As Object, ByVal args As PeekCompletedEventArgs)

        Dim oQueue As MessageQueue
        Dim oMessage As Message

        ' Retrieve the queue from which the message originated
        oQueue = CType(s, MessageQueue)

            oMessage = oQueue.EndPeek(args.AsyncResult)
            If oMessage.LookupId <> lastid Then
                ' Process the message here
                lastid = oMessage.LookupId
                ' let's write it out
                log.write(oMessage)
            End If

        objQueue.BeginPeek()
    End Sub

Have you tried using MSMQEvent.Arrived to track the messages? 您是否尝试过使用MSMQEvent.Arrived来跟踪消息?

The Arrived event of the MSMQEvent object is fired when the MSMQQueue.EnableNotification method of an instance of the MSMQQueue object representing an open queue has been called and a message is found or arrives at the applicable position in the queue. 当已调用表示打开队列的MSMQQueue对象的实例的MSMQQueue.EnableNotification方法并且找到消息或到达队列中的适用位置时,将触发MSMQEvent对象的Arrived事件。

A Thread.Sleep(10) in between peek iterations may save you a bunch of cycles. 窥视迭代之间的Thread.Sleep(10)可以为您节省一堆周期。

The only other option I can think of is to build the logging into the queue reading application. 我能想到的唯一其他选择是将日志记录构建到队列读取应用程序中。

There's no API that will let you peek at each message only once. 没有API可以让你只查看每条消息一次。

The problem is that BeginPeek executes its callback immediately if there's already a message on the queue. 问题是,如果队列中已有消息, BeginPeek立即执行其回调。 Since you aren't removing the message (this is peek after all, not receive!), when your callback begins peeking again the process starts over, so MessageFound runs almost constantly. 由于你没有删除消息(毕竟这是偷看 ,而不是接收!),当你​​的回调再次开始偷看时,进程重新开始,所以MessageFound几乎不停地运行。

Your best options are to log the messages in the writer or the reader. 您最好的选择是在编写器或阅读器中记录消息。 Journaling will work for short periods (if you only care about messages that are received), but aren't a long-term solution: 日记将在短期内工作(如果您只关心收到的消息),但不是长期解决方案:

While the performance overhead of retrieving messages from a queue that is configured for Journaling is only about 20% more than retrieving messages without Journaling, the real cost is unexpected problems caused when an unchecked MSMQ service runs out of memory or the machine is out of disk space 从为日记记录配置的队列中检索消息的性能开销仅比检索没有日记记录的消息多20%左右,但实际成本是未经​​检查的MSMQ服务内存不足或计算机磁盘空闲时导致的意外问题空间

This works for me. 这适合我。 It blocks the thread while waiting for a message. 它在等待消息时阻塞线程。 Each loop cycle checks the class member _bServiceRunning to see if the thread should abort. 每个循环周期检查类成员_bServiceRunning以查看该线程是否应该中止。

    private void ProcessMessageQueue(MessageQueue taskQueue)
    {
        // Set the formatter to indicate body contains a binary message:
        taskQueue.Formatter = new BinaryMessageFormatter();

        // Specify to retrieve selected properties.
        MessagePropertyFilter myFilter = new MessagePropertyFilter();
        myFilter.SetAll();
        taskQueue.MessageReadPropertyFilter = myFilter;

        TimeSpan tsQueueReceiveTimeout = new TimeSpan(0, 0, 10); // 10 seconds

        // Monitor the MSMQ until the service is stopped:
        while (_bServiceRunning)
        {
            rxMessage = null;

            // Listen to the queue for the configured duration:
            try
            {
                // See if a message is available, and if so remove if from the queue if any required
                // web service is available:
                taskQueue.Peek(tsQueueReceiveTimeout);

                // If an IOTimeout was not thrown, there is a message in the queue
                // Get all the messages; this does not remove any messages
                Message[] arrMessages = taskQueue.GetAllMessages();

                // TODO: process the message objects here;
                //       they are copies of the messages in the queue
                //       Note that subsequent calls will return the same messages if they are
                //       still on the queue, so use some structure defined in an outer block
                //       to identify messages already processed.

            }
            catch (MessageQueueException mqe)
            {
                if (mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
                {
                    // The peek message time-out has expired; there are no messages waiting in the queue
                    continue; // at "while (_bServiceRunning)"
                }
                else
                {
                    ErrorNotification.AppLogError("MSMQ Receive Failed for queue: " + mqs.Name, mqe);
                    break; // from "while (_bServiceRunning)"
                }
            }
            catch (Exception ex)
            {
                ErrorNotification.AppLogError("MSMQ Receive Failed for queue: " + mqs.Name, ex);
                break; // from "while (_bServiceRunning)"
            }
        }

    } // ProcessMessageQueue()

IMHO you should just turn on journaling on the queue. 恕我直言,你应该打开队列上的日记。 Then you are guaranteed a copy is kept of all messages that have been committed to the queue, and that just isn't the case with your laborous attempt to make your own mechanism to log it all. 然后,您可以保证保留所有已提交到队列的消息的副本,并且您不必尝试使用自己的机制来记录所有消息。

Much, much easier and more reliable is to log and remove the journaled messages on a scheduled basis if you want something more easily readable than the queue itself (and I certainly would want that). 如果你想要比队列本身更容易读取的东西(我当然希望这样),那么更容易,更可靠的是按计划记录和删除日志消息。 Then it doesn't really matter how fast or not the process is working, you only need to get the messages once, and it's overall just a much better way to solve the problem. 然后,进程工作的速度和速度并不重要,您只需要获取一次消息,而这总体上只是解决问题的一种更好的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM