How can I subscribe to an MSMQ queue but only "peep" into a .Net message?
We have an MSMQ queue setup that receives messages and is processed by the application. We would like another process to subscribe to the queue, and just read the message and write its contents.
I already have this, the problem is that it constantly looks into the queue. The CPU on the server when it is running is about 40%. Mqsvc.exe runs at 30% and this application runs at 10%. I would prefer something that just waits for a message to come in, gets notified about it, and then logs it without polling the server constantly.
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 write it out
log.write(oMessage)
End If
objQueue.BeginPeek()
End Sub
a source to share
Have you tried using MSMQEvent.Arrived to track messages?
An inbound event on an MSMQEvent is fired when the MSMQQueue.EnableNotification method of an instance of an MSMQQueue object representing an open queue has been called and a message has been found or arrives at the appropriate position in the queue.
a source to share
There is no API that will allow you to look at each message only once.
The problem is that it BeginPeek
executes the callback immediately if there is already a message in the queue. Since you are not deleting the message (this is peeking after all, don't accept!), When your callback starts peeking again, the process starts, so it MessageFound
runs almost constantly.
Your best options are to write messages to the writer or reader. Logging will work for short periods of time (if you only care about the messages received), but is not a long-term solution:
While the performance overhead of fetching messages from a queue that is configured for logging is only about 20% greater than fetching a message without logging, the real costs are unexpected problems caused when an unverified MSMQ service is started from memory or on a computer no disk space
a source to share
This works for me. It blocks the thread while waiting for a message. Each loop of the loop checks a class member _bServiceRunning
to see if the thread should be interrupted.
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()
a source to share
IMHO you should just turn on queue logging. Then you are guaranteed a copy of all the messages that were submitted to the queue, and that just isn't the case with your difficult attempt to make your own mechanism to log it all.
It is much easier and more reliable to log and delete logged messages on a schedule if you want something more readable than the queue itself (and I certainly would). Then it doesn't matter how fast or not the process works, you only need to receive messages once, and in general this is a much better way to solve the problem.
a source to share