![](/img/trans.png)
[英]MSMQ Application does not process queued messages when running as Windows Service
[英]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有什么關系,和/或是否可以省略它?
另一個問題是如果使用私有隊列,instad公共會解決什么問題?
性能問題。
您沒有提及服務器上是否正在運行所有代碼,或者您是否有客戶端遠程訪問服務器上的隊列。 從速度上來說,我會假設后者。
隊列是交易的嗎?
消息有多大?
如果要從隊列中讀取消息,則應用程序不會連接到隊列本身。 一切都在本地隊列管理器和遠程隊列管理器之間進行。 隊列管理器是寫入隊列和從隊列讀取的唯一進程。 因此,具有多個隊列或單個隊列不一定會有任何不同的執行。
因此,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.