Using Microsoft Message Queues to build Scalable Solutions



Abstract

One of the challenges that any Architect/Developer faces in designing a multi-tier distributed applications is scalability.  This article shows you how to create, send, and received messages using MSMQ from the .NET base class library (System.Messaging) and C#.  By architecting a multi-tier distributed application to used message queues among decoupling layers, is one of the elements that makes the final solution more scalable and robust.


Introduction

This project uses threads to simulate server components and client components.  The idea behind this is to demonstrate the use of message queue from a client(s) component sending multiple messages (requests) to a pool of server component(s).  The server component(s) will listen to the queue until a new client request is received.  Upon arrival of the client request, the receiving server component will service the client request by displaying the received message in a list box.  In this way, multiple client requests may be serve by multiple server component(s). 

If multiple requesting client(s) generates more requests than what the available pool of server component(s) can handles, the client requests will be queued until the next available server component can process it in the order in which the client request was received.  In this scenario, by spawning more server component(s) the client request can be server at much faster ratio (throughput), thus, the client response time is improved.  Notice that regardless of the balance between client and server component(s), no client request is lost or not processed.

Implementation

The project contains a control form (frmControl.cs), a server form (frmServerQueue.cs), and a client form (frmClientQueue.cs).

From the control form (see below) you can create a private message queue (on the specified local machine using the specified queue name) by pressing the create queue button (provided that is not disable because the queue already exist).  The host name is populated with the current DNS Host.


public frmControl()
{
tbServerName.Text = System.Net.DNS.GetHostName();
btnCreateQ.Enabled = (!(MessageQueue.Exists(tbServerName.Text + @"\private$\" +
bQueueName.Text)));
}

To create a message queue, just use the
class MessageQueue.Create() using one of the overloaded create method specify the path.
if (!(MessageQueue.Exists(tbServerName.Text + @"\private$\" + tbQueueName.Text)))
{
MessageQueue msgq = MessageQueue.Create(tbServerName.Text + @"\private$\" + tbQueueName.Text);
}


 Messag1.jpg

Messag2.jpg 

By pressing the Spawn Server button on the control form, you will be able to create up to 5 new instances of frmServerQueue on it own thread (one at the time).  All server object are place in a array of frmServerQueue[], an unique ID is assign to a property using accessors (this ID also identify the thread[index]), a message queue path that the server is going to listen to is assign to a property, also all server components raise an event to notify the control form (more on that later),  a new thread is created pointing to RetrieveMsg method, and the thread is started (see below).

if
(sqi <= 4)
{
sq[sqi] =
new
frmServerQueue();
sq[sqi].SQID = sqi;
sq[sqi].QueuePath = (tbServerName.Text + @"\private$\" + tbQueueName.Text);
sq[sqi].ServerEvent +=
new
frmServerQueue.ServerDelegate(EventServer);
tsq[sqi] =
new Thread(new
ThreadStart(sq[sqi].RetrieveMsg));
((frmServerQueue)sq[sqi]).Activate();
tsq[sqi].Start();
sqi++;
}

Messag3.jpg

Server form: End button terminate thread and close form Clear button clear the list box content (client messages).

By pressing the Spawn Client button on the control form, you will create up to 5 new instances of frmClientQueue on it own thread.  All client object are place in a array of frmClientQueue[], an unique ID is assign to a property using accessors (this ID also identify the thread[index]), a message queue path (that the client is going to use to send messages) is assign to a property, also all client components raise an event to notify the control form (more on that later),  a new thread is created pointing to SendMsg method, and the thread is started (see below).

if (cqi <= 4)
{
cq[cqi] =
new
frmClientQueue();
cq[cqi].SQID = cqi;
cq[cqi].QueuePath = (tbServerName.Text + @"\private$\" + tbQueueName.Text);
cq[cqi].ClientEvent +=
new
frmClientQueue.ClientDelegate(EventClient);
tcq[cqi] =
new Thread(new ThreadStart(cq[cqi].SendMsg));
((frmClientQueue)cq[cqi]).Activate();
tcq[cqi].Start();
cqi++;


Messag4.jpg

Event and Thread Management


Client and Server form publish an event that the control form subscribe to it passing a delegate function (EventClient, EventServer) respectively.  The client and server form raises these events right before closing the form and aborting the thread. 

protected
void btnClose_Click (object sender, System.EventArgs e)
{
this.ClientEvent(this, this
.sqid);
this
.Close();
Thread.CurrentThread.Abort();
}
protected void btnEndServer_Click (object
sender, System.EventArgs e)
{
this.ServerEvent(this,this
.sqid);
this
.Close();
Thread.CurrentThread.Abort();
}

The Control form upon receiving the arguments in it event handlers (client and server) destroy the pointer to the client or server instance that it holds in the array (using the ID as index), thus, the last reference to the object is been removed, therefore the GC can collect the heap space.

protected
void EventClient(object sender, int sqid)
{
cq[sqid] =
null
;
}
protected void EventServer(object sender, int
sqid)
{
sq[sqid] =
null
;
}

Conclusion

In this article you saw how to create, send, and received messages through MSMQ using .NET and C#.  Hopefully through this you will see the benefits of using message queues as a decouple mechanism of transferring messages/transactions among layers of a multi-tier apps, However, I have just scratch the surface of what MSMQ can do for you, it was my intention to motivate your curiosity and an invitation to investigate more of this topic (see MSMQ Documentation, and .NET Framework System.Messaging namespace).  As a secondary topic, I also wanted to show how events can be used in conjunction with a form, and how can it be leverage to manage a thread of execution.

Reference:
 A Programmers Introduction to C#  [Eric Gunnerson]
 C# Programming with the Public Beta [Wrox]
 C# Essentials [OReilly]
 Microsoft .Net Framework Documentation.

Next Recommended Readings