Order Delivery Application By Azure Service Bus Queue

Through this blog, I'm going to demonstrate how an Azure Service Bus Queue can be utilized to create a multi-tier application.

Introduction

In this article, I am implementing multi-tier application of sending and receiving messages through the Azure Service Bus queue.

Azure Service Bus queue is being used as a repository for First In First Out (FIFO) message processing. A sender sends a message to a queue, where it waits until a receiver picks it.

Background

To demonstrate “Service Bus” I will use a popular Pizza Order Delivery process. Example: When a customer orders pizza, the order will stay in the queue until the order is picked to be processed.

I named the application "Amazing Pizza Order Delivery". Pizza Orders will be placed by the customers through an MVC portal (Web Role). The order will be processed by a background processor (Worker Role).

Implementation

First  you need to have an Azure Account to implement the Service Bus. Once your account is set up, go to Azure UI and click on "Create a resource". Please note that the Azure Layout changes time to time so it might look different at the time you are reading this article.

On search bar, type "service bus" and find "service bus resource", select it and click "Create".
 
Order Delivery Application by Azure Service Bus Queue

Then Click on "+ Add" button and enter a unique Service Bus Name.

In this case, opted for "AmazingServiceBus". Select appropriate pricing, subscription, and location (nearest geographical). Select an existing resource group or create a new one.
 
Order Delivery Application by Azure Service Bus Queue

When all necessary information is filled out, click on "Create service bus namespace". It will start creating the namespace.

The notification will show "Deployment" and then "Success" message.

Deployment in progress...
 
 Order Delivery Application by Azure Service Bus Queue
 Deployment successful...
 
 Order Delivery Application by Azure Service Bus Queue
We will configure "Shared access policies" in the namespace. For simplicity, I will leave the default settings. These settings will be used to access "Service Bus". Note down the "Primary Key" and "Primary connection string" to use it later in the application.
 
 Order Delivery Application by Azure Service Bus Queue
 
Add a "Queue" in Service Bus. For this example, In this instance "DeliveryOrderQueue".
 
Order Delivery Application by Azure Service Bus Queue

Once the "Queue" is set up in Azure, let's start creating a Web UI.

Open VS (VS 2017) in admin mode and select "Azure Cloud Service", name the application "AmazingDeliveryService".
 
Order Delivery Application by Azure Service Bus Queue
 
Follow the prompt and select "ASP.NET web role" from Azure Cloud Service as we are developing an MVC application. Change the name from "WebRole1" to "AmazingFrontEndWebRole".
 
 Order Delivery Application by Azure Service Bus Queue
 
 Select MVC application with "No Authentication".
 
Order Delivery Application by Azure Service Bus Queue
 
 Once the MVC application is set up, goto reference, right click, select "Manage NuGet Package" and search for "Windows.Azure.ServiceBus".
 
Order Delivery Application by Azure Service Bus Queue
 
 Add a class "Connector.cs" to your project, keep class name "Connector", and add this code. This will be used to connect to "Azure Bus Service" to add a message to the queue.
  1. public static class Connector  
  2. {  
  3.     public const string NameSpace = "amazingservicebus"// service bus name  
  4.   
  5.     public const string QueueName = "deliveryorderqueue"// queue name  
  6.   
  7.     public const string key = "Primary key from servicebus";  
  8.   
  9.     public static QueueClient OrdersQueueClient;  
  10.   
  11.     public static NamespaceManager CreateNamespaceManager()  
  12.     {  
  13.         var uri = ServiceBusEnvironment.CreateServiceUri(  
  14.             "sb", NameSpace, String.Empty);  
  15.   
  16.         var tP = TokenProvider.CreateSharedAccessSignatureTokenProvider(  
  17.             "RootManageSharedAccessKey", key);  
  18.   
  19.         return new NamespaceManager(uri, tP);  
  20.     }  
  21.   
  22.     public static void Initialize()  
  23.     {  
  24.         ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;  
  25.   
  26.         var namespaceManager = CreateNamespaceManager();  
  27.   
  28.         var messagingFactory = MessagingFactory.Create  
  29.              (namespaceManager.Address, namespaceManager.Settings.TokenProvider);  
  30.   
  31.         OrdersQueueClient = messagingFactory.CreateQueueClient(QueueName);  
  32.     }  
  33. }  

Now, I will create a class for message transfer. I will call it "DeliveryModel" and will add it in "DeliveryModel.cs" file in model folder. Display name will be used to change the labels on Web UI.

Note: We will be using the same file to send and receive message
  1. public class DeliveryModel  
  2. {  
  3.     [DisplayName("Customer Name")]  
  4.     public string CustomerName { getset; }  
  5.   
  6.     [DisplayName("Product Name")]  
  7.     public string ProductName { getset; }  
  8. }  

 Now change the "HomeController" class

  1. public class HomeController : Controller  
  2. {  
  3.     public ActionResult Index()  
  4.     {  
  5.         return RedirectToAction("Submit");  
  6.     }  
  7.   
  8.     public ActionResult About()  
  9.     {  
  10.         ViewBag.Message = "Your application description page.";  
  11.   
  12.         return View();  
  13.     }  
  14.   
  15.     public ActionResult Contact()  
  16.     {  
  17.         ViewBag.Message = "Your contact page.";  
  18.   
  19.         return View();  
  20.     }  
  21.   
  22.     public ActionResult Submit()  
  23.     {  
  24.         var namespaceManager = Connector.CreateNamespaceManager();  
  25.   
  26.         var queue = namespaceManager.GetQueue(Connector.QueueName);  
  27.   
  28.         ViewBag.MessageCount = queue.MessageCount;  
  29.   
  30.         return View();  
  31.     }  
  32.   
  33.     [HttpPost]  
  34.     [ValidateAntiForgeryToken]  
  35.     public ActionResult Submit(DeliveryModel order)  
  36.     {  
  37.         if (ModelState.IsValid)  
  38.         {  
  39.             var message = new BrokeredMessage(order);  
  40.             Connector.OrdersQueueClient.Send(message);  
  41.             return RedirectToAction("Submit");  
  42.   
  43.         }  
  44.         else  
  45.         {  
  46.             return View(order);  
  47.         }  
  48.     }  
  49. }  

 Right click on Submit and add a "Submit.cshtml" view. Tie Delivery Model as Model so it can generate automatic UI.

Order Delivery Application by Azure Service Bus Queue
 
 Change the "Submit.cshtml" to be like this...
  1. @model AmazingFrontendWebRole.Models.DeliveryModel  
  2.   
  3. @{  
  4.     ViewBag.Title = "Product Order";  
  5. }  
  6.   
  7. <h2>Submit</h2>  
  8.   
  9.   
  10. @using (Html.BeginForm())   
  11. {  
  12.     @Html.AntiForgeryToken()  
  13.       
  14.     <div class="form-horizontal">  
  15.         <h4>Please input Customer Name & Product Name,   
  16.         then click Order. Total Order Count : @ViewBag.MessageCount </h4>  
  17.         <hr />  
  18.         @Html.ValidationSummary(true, "", new { @class = "text-danger" })  
  19.         <div class="form-group">  
  20.             @Html.LabelFor(model => model.CustomerName,   
  21.             htmlAttributes: new { @class = "control-label col-md-2" })  
  22.             <div class="col-md-10">  
  23.                 @Html.EditorFor(model => model.CustomerName,   
  24.                 new { htmlAttributes = new { @class = "form-control" } })  
  25.                 @Html.ValidationMessageFor(model => model.CustomerName, "",   
  26.                 new { @class = "text-danger" })  
  27.             </div>  
  28.         </div>  
  29.   
  30.         <div class="form-group">  
  31.             @Html.LabelFor(model => model.ProductName,   
  32.             htmlAttributes: new { @class = "control-label col-md-2" })  
  33.             <div class="col-md-10">  
  34.                 @Html.EditorFor(model => model.ProductName,   
  35.                 new { htmlAttributes = new { @class = "form-control" } })  
  36.                 @Html.ValidationMessageFor(model => model.ProductName,   
  37.                 "", new { @class = "text-danger" })  
  38.             </div>  
  39.         </div>  
  40.   
  41.         <div class="form-group">  
  42.             <div class="col-md-offset-2 col-md-10">  
  43.                 <input type="submit"   
  44.                 value="Order" class="btn btn-default" />  
  45.             </div>  
  46.         </div>  
  47.     </div>  
  48. }  
  49.   
  50. <div>  
  51.     @Html.ActionLink("Back to List", "Index")  
  52. </div>  
  53.   
  54. @section Scripts {  
  55.     @Scripts.Render("~/bundles/jqueryval")  
  56. }  

Change the "_Layout.cshtml" file to remove unwanted options...

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <title>@ViewBag.Title - My ASP.NET Application</title>  
  7.     @Styles.Render("~/Content/css")  
  8.     @Scripts.Render("~/bundles/modernizr")  
  9. </head>  
  10. <body>  
  11.     <div class="navbar navbar-inverse navbar-fixed-top">  
  12.         <div class="container">  
  13.             <div class="navbar-header">  
  14.                 <button type="button" class="navbar-toggle"   
  15.                 data-toggle="collapse" data-target=".navbar-collapse">  
  16.                     <span class="icon-bar"></span>  
  17.                     <span class="icon-bar"></span>  
  18.                     <span class="icon-bar"></span>  
  19.                 </button>  
  20.                 @Html.ActionLink("Product Order", "Index", "Home",   
  21.                 new { area = "" }, new { @class = "navbar-brand" })  
  22.             </div>  
  23.             <div class="navbar-collapse collapse">                  
  24.             </div>  
  25.         </div>  
  26.     </div>  
  27.     <div class="container body-content">  
  28.         @RenderBody()  
  29.         <hr />  
  30.         <footer>  
  31.             <p>© @DateTime.Now.Year - My ASP.NET Application</p>  
  32.         </footer>  
  33.     </div>  
  34.   
  35.     @Scripts.Render("~/bundles/jquery")  
  36.     @Scripts.Render("~/bundles/bootstrap")  
  37.     @RenderSection("scripts", required: false)  
  38. </body>  
  39. </html>  

 Call "Connector.Initialize()" in Application_Start() method.

  1. protected void Application_Start()  
  2. {  
  3.     AreaRegistration.RegisterAllAreas();  
  4.     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
  5.     RouteConfig.RegisterRoutes(RouteTable.Routes);  
  6.     BundleConfig.RegisterBundles(BundleTable.Bundles);  
  7.   
  8.     Connector.Initialize();  
  9. }  
Once all these have been set up, run the application and the UI will look like this.
 
 Order Delivery Application by Azure Service Bus Queue

When you submit an order, you will see the total number of orders in "Total Order Count".

You will also be able to see the total number of messages available in a queue in Azure.
 
Order Delivery Application by Azure Service Bus Queue

Let's create an application to consume messages. Remember these messages will stay in the queue until consumer doesn't consume.

To create a consumer, we will use Cloud application but this time with a worker role. Create a new VS cloud application named "DeliveryProvider" assuming this will only consume orders from Service Bus Queue.
 
Order Delivery Application by Azure Service Bus Queue
 
Select "Worker Role with Service Bus Queue", change the name of service bus to "DeliveryProviderServiceBus"
 
Order Delivery Application by Azure Service Bus Queue
 
Once the solution is ready, go to Roles folder and right-click on "DeliveryProviderServiceBus" and open the properties window.
 
Order Delivery Application by Azure Service Bus Queue
Go to settings in properties and change the value of "Microsoft.ServiceBus.ConnectionString" to the value of Primary Key from Azure's Shared Access Policies window
 
Order Delivery Application by Azure Service Bus Queue
 
 After that, add "DeliveryModel.cs" file to your project. This is the same file we used to send a message to the queue from MVC application. Note: Don't change namespace or class name.
  1. public class DeliveryModel  
  2. {  
  3.     [DisplayName("Customer Name")]  
  4.     public string CustomerName { getset; }  
  5.   
  6.     [DisplayName("Product Name")]  
  7.     public string ProductName { getset; }  
  8. }  
 Change the code of "WorkerRole.cs" file to be like this
  1. public class WorkerRole : RoleEntryPoint  
  2. {  
  3.     // The name of your queue  
  4.     const string QueueName = "deliveryorderqueue";  
  5.   
  6.     QueueClient Client;  
  7.     ManualResetEvent CompletedEvent = new ManualResetEvent(false);  
  8.   
  9.     public override void Run()  
  10.     {  
  11.         Trace.WriteLine("Starting processing of messages");  
  12.   
  13.         Client.OnMessage((receivedMessage) =>  
  14.         {  
  15.             try  
  16.             {  
  17.                 Trace.WriteLine("Processing", receivedMessage.SequenceNumber.ToString());  
  18.   
  19.                 var order = receivedMessage.GetBody<DeliveryModel>();  
  20.   
  21.                 Trace.WriteLine(order.CustomerName + ": " + order.ProductName, "ProcessingMessage");  
  22.   
  23.                 receivedMessage.Complete();  
  24.             }  
  25.             catch (Exception ex)  
  26.             {  
  27.                     // Handle any message processing specific exceptions here  
  28.                 }  
  29.         });  
  30.   
  31.         CompletedEvent.WaitOne();  
  32.     }  
  33.   
  34.     public override bool OnStart()  
  35.     {  
  36.         // Set the maximum number of concurrent connections   
  37.         ServicePointManager.DefaultConnectionLimit = 4;  
  38.   
  39.         string connectionString = CloudConfigurationManager.GetSetting  
  40.                                     ("Microsoft.ServiceBus.ConnectionString");  
  41.   
  42.         var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);  
  43.   
  44.         Client = QueueClient.CreateFromConnectionString(connectionString, QueueName);  
  45.   
  46.         return base.OnStart();  
  47.     }  
  48.   
  49.     public override void OnStop()  
  50.     {  
  51.         Client.Close();  
  52.         CompletedEvent.Set();  
  53.         base.OnStop();  
  54.     }  
  55. }  

As you will see, the code I am trying to connect "deliveryorderqueue" from the connection string specified in Properties->Settings.

Go ahead and run this application. This will run in Azure Emulator in a local machine. Once it runs, it will start consuming orders from the "deliveryorderqueue". When you go to the Azure queue count, the number of orders will be reduced by the number of messages that have been read.