beaucrawford.net

Give me data or give me death

About the author

Author Name is someone.
E-mail me Send mail

Recent comments

Don't show

Authors

Tags

Don't show

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2012

    MSMQ – Sending Messages to Remote Queues

    The situation is simple – you want to send a message to a remote MSMQ. To do so you might write some code that looks something like:

    using (var queue = new MessageQueue(@"FormatName:Direct=TCP:192.168.2.157\Private$\TheQueue"))
    {	
    	var message = new Message(shipment);
    	message.Formatter = new BinaryMessageFormatter();
    	queue.Send(message, MessageQueueTransactionType.Single);	
    }

    This code works fine... most of the time. The problem is that, for remote queues, MSMQ sends messages in a "fire and forget" type of mode by default, i.e. if the message gets there, great! If not, oh damn well. This was not apparent to me at first. In workflow situations where you cannot let things "slip through the cracks" it is critical that you receive acknowledgement of delivery. For example, consider the following pseudo-code:

    using (var transaction = new TransactionScope())
    {
        // Perform numerous database operations here
    
        // Send message to remote queue here
    
        transaction.Complete();
    }

    What if the send operation to the remote queue does not succeed? Well, in a workflow with state tracking, we absolutely must rollback all of the database operations and leave the system in the exact same state that existed before we attempted the send operation. The only way we can do this is to know whether the message arrived in the remote queue. Thankfully MSMQ provides a fairly easy way to accomplish this through the use of an Administrative Queue. The purpose of this queue is to provide a "callback" location for the target queue to send an acknowledgement message. This message is uniquely identified by its Message ID property. The high-level steps for guaranteed delivery are:

    1) Create a message and define an Administrative Queue 
    2) Send the message 
    3) Wait a little 
    4) Check the Administrative Queue for the message's unique ID

    The code to do this is:

    using (var queue = new MessageQueue(@"FormatName:Direct=TCP:192.168.2.157\Private$\TheQueue"))
    {
    	var message = new Message(shipment);
    	message.Formatter = new BinaryMessageFormatter();
    	message.AdministrationQueue = new MessageQueue(@"FormatName:Direct=TCP:192.168.2.148\Private$\AdminQueue");
    	message.AcknowledgeType = AcknowledgeTypes.PositiveArrival;
    	queue.Send(message, MessageQueueTransactionType.Single);
    
    	Thread.Sleep(100);
    
    	bool acknowledged = ReceiveAcknowledgment(message.Id, @".\Private$\AdminQueue");
    
    	if (!acknowledged)
    	{
    	   throw new InvalidOperationException("Acknowledgement was not received");
    	}
    }

    Yes, the above code uses a Thread.Sleep call. I realize that this is somewhat of an anti-pattern but it is called for here as you have no way of gauging network latency.  The best you can really do is supply a sleep time that is relative to your queue architecture, i.e. if your queues are on an Intranet then you can probably get away with 100 ms (or less).  If you’re using queues over the Internet then you will probably need a longer wait time.  This is the price you pay for guaranteed delivery acknowledgement – a small one in my opinion.

    The "ReceiveAcknowledgment" helper method is defined as follows:

    private static bool ReceiveAcknowledgment(string messageId, string queuePath)
    {
        var queue = new MessageQueue(queuePath);
        queue.MessageReadPropertyFilter.CorrelationId = true;
        queue.MessageReadPropertyFilter.Acknowledgment = true;
    
        while (queue.PeekByCorrelationId(messageId) != null)
    	{
    		Message message = queue.ReceiveByCorrelationId(messageId);
    		return true;
    	}
    
        return false;
    }

    Categories: C#
    Posted by Beau on Saturday, June 27, 2009 11:43 AM
    Permalink | Comments (1) | Post RSSRSS comment feed

    Comments

    justin chase us

    Saturday, June 27, 2009 6:59 PM

    Shouldn't that last while loop be an if statement? Or should you be checking to see if message is not null before returning true?