簡體   English   中英

當有大量消息排隊時,從MSMQ讀取速度會變慢

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

簡短介紹

我有一個基於SEDA的系統,並使用MSMQ在不同的應用程序/服務之間進行通信(事件觸發)。

其中一個服務按文件獲取消息,因此我有一個文件監聽器,它讀取文件內容並將其插入隊列(或實際上是4個不同的隊列,但這對第一個問題不是很重要)。

服務器是Windows Server 2008

第一個問題 - 閱讀速度慢下來

我在另一端讀取這些消息的應用程序通常每秒從隊列中讀取大約20條消息,但是當發布消息的服務開始排隊數千條消息時,讀取​​將關閉,並且讀取應用程序僅讀取2-4條消息第二。 當沒有發布到隊列時,讀取的應用程序可以再次讀取每秒最多20條消息。

閱讀應用程序中的代碼非常簡單,在C#中開發,我在System.Messaging中使用Read(TimeSpan超時)函數。

問:為什么在隊列中發布大量消息時讀取速度會變慢?

第二個問題 - TPS的局限性

另一個問題是閱讀本身。 如果我使用1或5個線程從隊列中讀取,那么我每秒可以讀取的消息數似乎沒有區別。 我也試過實現一個“循環解決方案”,其中郵政服務發布到一組隨機的4個隊列,並且讀取的應用程序有一個線程監聽這些隊列中的每一個,但即使我仍然只有20個TPS從1個隊列中讀取1個線程,1個隊列,4個線程或4個隊列(每個隊列一個線程)。

我知道線程中的處理大約需要50毫秒,所以如果當時只處理一條消息,那么20 TPS是完全正確的,但多線程的線索應該是並行處理消息而不是順序處理。

服務器上有大約110個不同的隊列。

問:為什么即使使用多線程和使用多個隊列,我也無法在隊列中收到超過20條消息?

這是今天運行的代碼:

// 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;
            }
        }
    }

但即使有4個線程,似乎所有人都等待該函數再次進入“接收”點。 我也嘗試使用4個不同的隊列(content1,content2,content3和content4),但我仍然每50毫秒處理1條消息。

這與Receive()中的TimeSpan有什么關系,和/或是否可以省略它?

另一個問題是如果使用私有隊列,in​​stad公共會解決什么問題?

性能問題。
您沒有提及服務器上是否正在運行所有代碼,或者您是否有客戶端遠程訪問服務器上的隊列。 從速度上來說,我會假設后者。
隊列是交易的嗎?
消息有多大?

如果要從隊列中讀取消息,則應用程序不會連接到隊列本身。 一切都在本地隊列管理器和遠程隊列管理器之間進行。 隊列管理器是寫入隊列和從隊列讀取的唯一進程。 因此,具有多個隊列或單個隊列不一定會有任何不同的執行。

因此,MSMQ隊列管理器在某些時候將成為瓶頸,因為它可以同時完成很多工作。 您的第一個問題顯示了這一點 - 當您在隊列管理器上放置高負載並將消息放入IN時,您獲取消息OUT的能力會降低。 例如,我建議查看性能監視器以查看MQSVC.EXE是否被最大化。

你為什么用時間跨度? - 這是一件壞事,這就是原因。

在開發服務和隊列時,您需要以安全的方式進行編程。 隊列中的每個項目都將生成一個新線程。 使用timespan強制每個線程使用單個計時器事件線程。 這些事件必須在事件線程中等待輪到他們。

規范是每個隊列事件1個線程 - 這通常是您的System.Messaging.ReceiveCompletedEventArgs事件。 另一個線程是你的onStart事件......

每秒20個線程或20個讀取可能是正確的。 通常在線程池時,您只能在.net中一次生成36個線程。

我的建議是刪除計時器事件,讓你的隊列簡單地處理數據。

做更像這樣的事;

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