简体   繁体   English

当有大量消息排队时,从MSMQ读取速度会变慢

[英]Reading from MSMQ slows down when there is a lot of messages queued

Short introduction 简短介绍

I have a SEDA based system, and used MSMQ for communication (event triggering) between the different applications/services. 我有一个基于SEDA的系统,并使用MSMQ在不同的应用程序/服务之间进行通信(事件触发)。

One of these services gets messages by file, so I have a file listener that reads the file content and inserts this into a queue (or actually 4 different queues, but that's not very important for the first question). 其中一个服务按文件获取消息,因此我有一个文件监听器,它读取文件内容并将其插入队列(或实际上是4个不同的队列,但这对第一个问题不是很重要)。

Server is Windows Server 2008 服务器是Windows Server 2008

First question - read slows down 第一个问题 - 阅读速度慢下来

My application that reads these messages at the other side normally reads about 20 messages from the queue per second, but when the service that posts messages start queuing some thousand messages, the read goes down, and the read application only reads 2-4 messages per second. 我在另一端读取这些消息的应用程序通常每秒从队列中读取大约20条消息,但是当发布消息的服务开始排队数千条消息时,读取​​将关闭,并且读取应用程序仅读取2-4条消息第二。 When there is no posting to the queue, the read application can again read up to 20 messages per second. 当没有发布到队列时,读取的应用程序可以再次读取每秒最多20条消息。

The code in the reading application is pretty simple, developed in C#, I use the Read(TimeSpan timeout) function in System.Messaging. 阅读应用程序中的代码非常简单,在C#中开发,我在System.Messaging中使用Read(TimeSpan超时)函数。

Q: Why does the read slows down when there is a lot of messages posted to the queue? 问:为什么在队列中发布大量消息时读取速度会变慢?

Second question - limitations of TPS 第二个问题 - TPS的局限性

An additional question is about the read itself. 另一个问题是阅读本身。 It seems there is no difference in how many messages I can read per second if I use 1 or 5 threads to read from the queue. 如果我使用1或5个线程从队列中读取,那么我每秒可以读取的消息数似乎没有区别。 I've also tried implementing a "round robin solution" where the post service are posting to a random set of 4 queues, and the read application had one thread listening to each of these queues, but there is still only 20 TPS even if I read from 1 queue with 1 thread, 1 queue with 4 threads or 4 queues (with one thread per queue). 我也试过实现一个“循环解决方案”,其中邮政服务发布到一组随机的4个队列,并且读取的应用程序有一个线程监听这些队列中的每一个,但即使我仍然只有20个TPS从1个队列中读取1个线程,1个队列,4个线程或4个队列(每个队列一个线程)。

I know the processing in the thread takes about 50 ms, so 20 TPS is quite correct if there is only one message processed at the time, but the clue with multi threading should be that messages are handled in parallel and not sequential. 我知道线程中的处理大约需要50毫秒,所以如果当时只处理一条消息,那么20 TPS是完全正确的,但多线程的线索应该是并行处理消息而不是顺序处理。

There is about 110 different queues on the server. 服务器上有大约110个不同的队列。

Q: Why can't I get more than 20 messages out of my queue at the time even with multi threading and the use of several queues? 问:为什么即使使用多线程和使用多个队列,我也无法在队列中收到超过20条消息?

This is the code running today: 这是今天运行的代码:

// There are 4 BackgroundWorkers running this function
void bw_DoWork(object sender, DoWorkEventArgs e) 
{
    using(var mq = new MessageQueue(".\\content"))
    {
        mq.Formatter = new BinaryMessageFormatter();

        // ShouldIRun is a bool set to false by OnStop()
        while(ShouldIRun)
        {
            try
            {
                using(var msg = mq.Receive(new TimeSpan(0,0,2))
                {
                    ProcessMessageBody(msg.Body); // This takes 50 ms to complete
                }
            }
            catch(MessageQueueException mqe)
            {
               // This occurs every time TimeSpan in Receive() is reached
               if(mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout) 
                   continue;
            }
        }
    }

But even if there are 4 threads, it seems all waits for the function to enter the "Receive" point again. 但即使有4个线程,似乎所有人都等待该函数再次进入“接收”点。 I've also tried using 4 different queues (content1, content2, content3 and content4), but still i get 1 message processed every 50 ms. 我也尝试使用4个不同的队列(content1,content2,content3和content4),但我仍然每50毫秒处理1条消息。

Does this have anything to do with the TimeSpan in Receive(), and/or is it possible to omit this? 这与Receive()中的TimeSpan有什么关系,和/或是否可以省略它?

Another question is if the use of private queues, instad of public will solve anything? 另一个问题是如果使用私有队列,in​​stad公共会解决什么问题?

Performance issues. 性能问题。
You don't mention if all the code is running on the server or if you have clients remotely accessing the queues on the server. 您没有提及服务器上是否正在运行所有代码,或者您是否有客户端远程访问服务器上的队列。 From the speed, I'll assume the latter. 从速度上来说,我会假设后者。
Also, are the queues transactional? 队列是交易的吗?
How large are the messages? 消息有多大?

If you want to read a message from a queue, your application does not connect to the queue itself. 如果要从队列中读取消息,则应用程序不会连接到队列本身。 Everything goes between the local queue manager and the remote queue manager. 一切都在本地队列管理器和远程队列管理器之间进行。 The queue manager is the only process that writes to, and reads from queues. 队列管理器是写入队列和从队列读取的唯一进程。 Therefore having multiple queues or a single queue won't necessarily perform any differently. 因此,具有多个队列或单个队列不一定会有任何不同的执行。

The MSMQ queue manager is therefore going to be a bottleneck at some point as there is only so much work it can do at the same time. 因此,MSMQ队列管理器在某些时候将成为瓶颈,因为它可以同时完成很多工作。 Your first question shows this - when you put a high load on the queue manager putting messages IN, your ability to take messages OUT slows down. 您的第一个问题显示了这一点 - 当您在队列管理器上放置高负载并将消息放入IN时,您获取消息OUT的能力会降低。 I'd recommend looking at performance monitor to see if MQSVC.EXE is maxed out, for example. 例如,我建议查看性能监视器以查看MQSVC.EXE是否被最大化。

Why are you using timespan? 你为什么用时间跨度? - that is a bad thing and here is why. - 这是一件坏事,这就是原因。

When developing services and queue you need to program in a theadsafe manner. 在开发服务和队列时,您需要以安全的方式进行编程。 Each item in the queue will spawn a new thread. 队列中的每个项目都将生成一个新线程。 Using timespan is forcing each of the threads to use a single timer event thread. 使用timespan强制每个线程使用单个计时器事件线程。 These events are having to wait for their turn at the event thread. 这些事件必须在事件线程中等待轮到他们。

The norm is 1 thread per queue events - This is generally your System.Messaging.ReceiveCompletedEventArgs event. 规范是每个队列事件1个线程 - 这通常是您的System.Messaging.ReceiveCompletedEventArgs事件。 Another thread is your onStart event... 另一个线程是你的onStart事件......

20 threads or 20 reads per second is probably correct. 每秒20个线程或20个读取可能是正确的。 Generally when thread pooling you can only spawn 36 threads at a time in .net. 通常在线程池时,您只能在.net中一次生成36个线程。

My advice is drop the timer event an make your queue simply process the data. 我的建议是删除计时器事件,让你的队列简单地处理数据。

do something more like this; 做更像这样的事;

namespace MessageService 
{ 

public partial class MessageService : ServiceBase 

{ 

    public MessageService() 

    { 

        InitializeComponent(); 

    } 



    private string MessageDirectory = ConfigurationManager.AppSettings["MessageDirectory"]; 

    private string MessageQueue = ConfigurationManager.AppSettings["MessageQueue"]; 



    private System.Messaging.MessageQueue messageQueue = null; 



    private ManualResetEvent manualResetEvent = new ManualResetEvent(true); 





    protected override void OnStart(string[] args) 

    { 

        // Create directories if needed 

        if (!System.IO.Directory.Exists(MessageDirectory)) 

            System.IO.Directory.CreateDirectory(MessageDirectory); 



        // Create new message queue instance 

        messageQueue = new System.Messaging.MessageQueue(MessageQueue); 



        try 

        {    

            // Set formatter to allow ASCII text 

            messageQueue.Formatter = new System.Messaging.ActiveXMessageFormatter(); 

            // Assign event handler when message is received 

            messageQueue.ReceiveCompleted += 

                new System.Messaging.ReceiveCompletedEventHandler(messageQueue_ReceiveCompleted); 

            // Start listening 



            messageQueue.BeginReceive(); 

        } 

        catch (Exception e) 

        { 



        } 

    } 



    protected override void OnStop() 

    { 

        //Make process synchronous before closing the queue 

        manualResetEvent.WaitOne(); 





        // Clean up 

        if (this.messageQueue != null) 

        { 

            this.messageQueue.Close(); 

            this.messageQueue = null; 

        } 

    } 



    public void messageQueue_ReceiveCompleted(object sender, System.Messaging.ReceiveCompletedEventArgs e) 

    { 

        manualResetEvent.Reset(); 

        System.Messaging.Message completeMessage = null; 

        System.IO.FileStream fileStream = null; 

        System.IO.StreamWriter streamWriter = null; 

        string fileName = null; 

        byte[] bytes = new byte[2500000]; 

        string xmlstr = string.Empty;                

            try 

            { 

                // Receive the message 

                completeMessage = this.messageQueue.EndReceive(e.AsyncResult);                    

                completeMessage.BodyStream.Read(bytes, 0, bytes.Length); 



                System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding(); 



                long len = completeMessage.BodyStream.Length; 

                int intlen = Convert.ToInt32(len);                   

                xmlstr = ascii.GetString(bytes, 0, intlen);                   

            } 

            catch (Exception ex0) 

            { 

                //Error converting message to string                    

            } 

        }

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

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