Using Microsoft Message Queues To Build Scalable Solutions

Abstract

One of the challenges that any Architect/Developer faces in designing a multi-tier distributed application is scalability. This article shows you how to create, send, and receive messages using MSMQ from the .NET base class library (System. Messaging) and C#. Architecting a multi-tier distributed application to use message queues among decoupling layers is one of the elements that make 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 a 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 served by multiple server component(s).

If multiple requesting client(s) generate more requests than what the available pool of server component(s) can handle, 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 served at a 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 of Message Queue

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 disabled because the queue already exists). 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$\\{tbQueueName.Text}");
}
// To create a message queue, use the MessageQueue.Create() method
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]), and a message queue path that the server is going to listen to is assigned 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

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 placed in an array of frmClientQueue[], a unique ID is assigned 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 assigned 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 forms publish an event that the control form subscribes 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 its event handlers (client and server), destroys 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 receive messages through MSMQ using .NET and C#. Hopefully, through this, you will see the benefits of using message queues as a decoupling mechanism for transferring messages/transactions among layers of multi-tier apps, However, I have just scratched 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 leveraged 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.


Similar Articles