Share via



March 2013

Volume 28 Number 03

Azure Insider - Real-World Scenarios for Node.js in Microsoft Azure

By Bruno Terkaly, Ricardo Villalobos | March 2013

Bruno Terkaly, Ricardo VillalobosThe popular quote, “If all you have is a hammer, everything looks like a nail,” certainly applies to software architecture. The best developers, however, understand a wide variety of frameworks, programming languages and platforms so they can engineer solutions that not only fulfill immediate business requirements, but also result in solutions that are scalable, maintainable, extensible and reusable. Node.js burst onto the scene three years ago, offering yet another tool for creating server-side software systems that support scalable Internet applications. Like all development tools, Node.js is not a magic hammer, and its capabilities should be fully understood before deciding if it’s the right fit for the solution at hand.

In case you’re new to Node.js, it’s a platform for building scalable network applications, based on the Google V8 JavaScript engine. It provides a single-threaded evented-io model, which allows orchestration of tasks running in parallel using an asynchronous/event-callback/non-blocking approach as shown in Figure 1. Node.js can be seen as a lightweight server that supports multiple connections without requiring a large memory footprint.

Node.js—Single-Threaded Model Based on an Asynchronous/Event-Callback/Non-Blocking Approach
Figure 1 Node.js—Single-Threaded Model Based on an Asynchronous/Event-Callback/Non-Blocking Approach

From a deployment perspective, the full Node.js engine is contained in a small executable—less than 5MB—that can be installed in Windows, Linux or Mac OS X. It implements a highly modularized architecture, including a few built-in components, such as those for listening to HTTP and TCP ports, making requests, or accessing the file system. Additional modules, provided by a strong open community, can be downloaded as needed, using the Node package manager utility (npm). Thanks to this approach, it’s possible to have an HTTP server up and running with five lines of code:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');}).listen(8080);
console.log('Server running on port 8080');

Having said this, there are a few significant challenges Node.js developers face. First, it forces a programming model based on asynchronous calls, running on a single thread. This is a paradigm shift from conventional programming, where tasks can be assigned to multiple threads. If care is not taken, programmers can get in trouble, either blocking the server or creating unexpected code behaviors. The second challenge is related to callbacks: Code can become unwieldy and difficult to maintain due to deep nesting. Last, debugging is not simple, particularly for complex scenarios. Even though there are approaches for working through each of these challenges, it takes conscious effort and learning. Keep in mind that Node.js is young; fortunately, there’s a robust community of programmers ready to help, as well as online resources such as howtonode.org.

At the end of the day, the best way to evaluate a new technology is in the real world, where problems are encountered and solved. In this article we present two specific cloud-based scenarios using Node.js, with Azure as the deployment platform. If you’re already a client-side JavaScript developer, you’ll hit the ground running from a language point of view.

Prerequisites

Before you start testing the solutions presented in this article, make sure you download the Node.js Windows installer, which can be found at nodejs.org/download. The installer will place two main files in your Windows Program Files folder: node.exe, which is the Node.js runtime, and the npm, which allows you to download third-party modules. For deploying Node.js solutions to Azure, download the corresponding command-line tools, which you’ll find at windowsazure.com/en-us/develop/downloads. Prerequisites that are specific to the use cases are included in each section.

Deploying Node.js Applications to Azure

Azure offers three cloud deployment models for applications to be deployed in any of the eight Microsoft global datacenters: Virtual Machines (VMs), Cloud Services and Web Sites. The most appropriate deployment model depends on the level of scale, control and flexibility you require. The price you pay for more scale, control and flexibility is that more work is involved to deploy and maintain your Node.js application. The Web Sites model frees the developer from worrying about firewall rules, virtual networks and OSes. Naturally, you give up fine-grained control of your deployment when you select this option.

Even though it’s impossible to cover all types of scenarios in a single diagram, Figure 2 shows a decision tree for determining where to deploy your Node.js solution, based on infrastructure and software components required by the application. We’ll use it to determine the Azure deployment model for the real-world examples in this article.

Decision Tree for Deploying Node.js Applications to Azure
Figure 2 Decision Tree for Deploying Node.js Applications to Azure

Real-World Scenario 1: Real-Time Web Communication Between HTTP Clients and Servers The first scenario illustrates how Node.js makes sense for applications that require real-time communication between Web browsers and HTTP servers, such as chat solutions, social media, news tickers and video games. Traditionally, developers have achieved this type of communication by using different long-term connection mechanisms, including long-polling and streaming. More recently, the HTML5 specification has included a communication protocol named WebSockets that provides full-duplex communications channels over a single TCP connection, but this is only supported by the latest versions of the most common Web browsers. Node.js applications can support real-time communication scenarios through a third-party module called socket.io, which supports multiple types of transports, including xhr-polling and WebSockets. Socket.io is based on an event-driven approach between the server and the Web browser clients, as depicted in Figure 3. It is well-documented at bit.ly/NlDOv7.

Real-Time Communication Between Web Browsers and HTTP Servers Using Node.js
Figure 3 Real-Time Communication Between Web Browsers and HTTP Servers Using Node.js

The basic flow is as follows:

  1. The Web client connects to the server and agrees on a protocol for communication (such as WebSockets, XMLHttpRequest (XHR), long-polling or flash sockets).
  2. The Web client sends an event to the Node.js server via JavaScript, using the socket.emit method.
  3. The server captures the event by matching the name of the function sent by the client to the one defined in any of its socket.on definitions.
  4. The server can respond to the client by using the socket.emit method, or broadcast messages to all the connected clients using socket.broadcast.emit.

This is illustrated in Figure 4 and Figure 5, which show the server-side code and client-side code, respectively. The code simply broadcasts a message to all connected clients.

Figure 4 Server-Side Code for Establishing Real-Time Communication

// Include needed packages (socket.io and express)
var express = require('express');
var app = express()
  , http = require('http')
  , server = http.createServer(app)
  , io = require('socket.io').listen(server);
// REPLACE BELOW var port = var port = process.env.PORT || 8080;
// Allow connections on port 8080, or the environment port number
var port = process.env.PORT || 8080;
// At the time of this writing, WebSockets is not supported
// in Azure Web Sites, which will force socket.io
// to fall back to a different communication protocol
// Prevent potential problems by specifying one, in this case, xhr-polling
io.set('transports', ['xhr-polling']);
// Listen for incoming requests
server.listen(port);
// Redirect request to index.html
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});
// When connected and sendmessage is called by client,
// broadcast data sent by one client to all connected clients
io.sockets.on('connection', function (socket) {
  // When the client emits 'sendmessage,' the following method is triggered
  socket.on('sendmessage', function (data) {
    // Message is broadcast to all clients
    socket.broadcast.emit('displaymessage', data);
  });
});

Figure 5 Client-Side Code for Establishing Real-Time Communication

<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
  // Initialize the socket connection
  var socket = io.connect();
  // Ask client (browser input box) to enter text      
  function sendMessage(){
    socket.emit('sendmessage', prompt("Message to broadcast?"));
  }
  // Displaymessage event received at all clients
  // display in alert dialog box
  socket.on('displaymessage', function(data){
    alert(data);
  });
</script>
</head>
<body>
<!—Client sends user input to node.js server through the sendMessage JavaScript function-->
<input type="button" value="Broadcast new message" onClick="sendMessage();">
</body>
</html>

Testing Your Application Locally

Follow these steps to test your application locally:

  1. Create a local folder called {drive letter}://nodejs/sockets.
  2. Using your preferred text editor, create a file called server.js.
  3. Copy and paste the code listed in Figure 4 for the server side.
  4. In the same directory, create a file called index.html.
  5. Copy and paste the code listed in Figure 5 for the client side.
  6. Open a command prompt, and change the directory to {drive letter}://nodejs/sockets.
  7. Make sure you’re connected to the Internet, and type npm install socket.io. This will install the required socket.io module.
  8. Type npm install express. Express is a module that simplifies access to the HTTP server functions, and can be easily integrated with socket.io.
  9. Type node server.js.
  10. Open a Web browser compatible with WebSockets and enter the URL https://localhost:8080.
  11. Open a second Web browser tab or window, pointing to the same URL.
  12. A message sent from the first client will be broadcast to all the other clients connected to the server.

Deploying Your Solution to Azure

Based on the decision tree in Figure 2, Azure Web Sites is a good option for our application (Linux isn’t required; no additional components in the OS are needed; and a hybrid infrastructure isn’t necessary to run the solution). Keep in mind that at the time of this writing, WebSockets is not supported in Azure Web Sites, so we’ve added a line in our code that specifies the communication protocol to be used by socket.io—we want to use the xhr-polling transport for the communication between the server and the Web browser clients. The easiest way to deploy an application to Azure Web Sites is by using Git, which can be downloaded at git-scm.com/download. Once you have it installed, go to the Azure portal at manage.windowsazure.com and create a new empty Web Site. Enable Git publishing by clicking on the corresponding option, as shown in Figure 6.

Enable Git Publishing for Your New Web Site
Figure 6 Enable Git Publishing for Your New Web Site

After a few seconds, a new screen will appear, showing the URL to the Git repository for your Web Site. If you haven’t set up any credentials for your account yet, you’ll need to provide them before continuing. The URL is in the form https://WindowsAzureAccount@WebSiteName.scm.azurewebsites.net/WebSiteName.git. Record it, because it will be used in the next deployment steps. Be sure you executed the local test before continuing, because the modules need to be downloaded first. (Note that Web Sites can also be created using the command-line tool for Mac and Linux, found at bit.ly/RGCc3A.) Once your Web Site has been created, follow these steps to deploy your Node.js solution to Azure:

  1. Open a command prompt and change your current folder to {drive letter}://nodejs/sockets (where you created your application).
  2. Type git init. This creates a local Git repository for your solution.
  3. Type git add. This adds the solution to your new local repository.
  4. Type git commit –m “Initial commitment.” This creates a pending Git commitment.
  5. Type git remote azure {URLforGITRepository}. Use the Git URL found in the Azure portal, which you previously recorded.
  6. Type git push azure master. Enter your password when requested.
  7. Wait until progress gets to 100 percent, and then your solution is ready to be used in the cloud.

Real-World Scenario 2: Creating a Quick and Robust RESTful Web Service Layer for Data Applications As we’ve discussed in previous articles, mobile solutions running on different devices (iOS, Android, Windows Phone) can be unified by making them talk to Web services that provide access to data operations in the back end. This usually requires a data layer that extracts or inserts information from or to the database, as well as a service layer that maps internal objects to UI objects (usually in JSON format). Even though this can be achieved by using traditional Web servers such as IIS and frameworks such as the ASP.NET Web API, Node.js offers a simpler solution for this scenario, acting as a simple orchestrator that delegates queries to the database engine and sends back responses in native JSON format, due to its JavaScript nature (see Figure 7).

Node.js Provides a Web Service Layer to Data Apps
Figure 7 Node.js Provides a Web Service Layer to Data Apps

Any objects obtained from the database can be easily returned in JSON format by using the res.json method. The example in Figure 8 defines a couple of RESTful calls for the HTTP server, reads data from a SQL Server database and returns the results in JSON format.

Figure 8 Exposing a Full RESTful API for a SQL Server Database Running on Azure

var sql = require('msnodesql');
var express = require('express');
var conn_str = "Driver={SQL Server Native Client 11.0};
  Server=[serverName].database.windows.net,1433;Database=AdventureWorks2012;
  Trusted_Connection={No};
  Uid=[LoginForDb]@[serverName];Pwd=[Password];Encrypt=yes";
var app = express();
var port = process.env.PORT || 8080;
app.get('/', function(req,res) {sql.query(conn_str, "SELECT FirstName,
  LastName FROM Person.Person", function (err, results) {
    if (err)
      console.log(err);
    else
      res.json(results);
  })
});     
app.get('/lastname/:lastname', function(req,res) {sql.query(conn_str,
  "SELECT FirstName, LastName FROM Person.Person WHERE LastName LIKE ?",
  [req.params.lastname], function (err, results) {
    if (err)
      console.log(err);
    else
    {
      for (var i = 0; i < results.length; i++) {
        res.json(results[i]);
      }
    }
  })
});
app.listen(port);
console.log("Server listening on port 8080");

In order to test this solution, deploy the AdventureWorks database to Azure by following the instructions at bit.ly/d0apaC. You’ll need to modify the connection string in the example in Figure 8 accordingly. To deploy the example to Azure, follow the same steps explained in the first scenario for testing locally and deploying to the cloud.

This is the simplest way to expose a full RESTful API for your data layer running on Azure. Even though we used a SQL Server database to illustrate this scenario, many other data engines are supported in Node.js, including Azure Table Storage, MongoDB and Cassandra, among others. Some of them, like MongoDB, are offered in an as-a-service model in the Azure store, which facilitates integration with applications deployed in the Microsoft cloud, including Node.js applications.

Wrapping Up

We’ve shown you two real-world scenarios where Node.js can be used for simple connectivity tasks, taking advantage of its single-­threaded approach and modules created by the community. The important thing to remember is that any synchronous blocking operation disrupts this model, and applications should be written with this in mind. In many cases, Node.js can be installed side-by-side with other engines, acting as an offload server for specific functionality inside the solution. Also, Node.js can be deployed using three different models in Azure, depending on the level of scalability and control required.

Get help building your Azure app!


Bruno Terkaly is a developer evangelist for Microsoft. His depth of knowledge comes from years of experience in the field, writing code using a multitude of platforms, languages, frameworks, SDKs, libraries and APIs. He spends time writing code, blogging and giving live presentations on building cloud-based applications, specifically using the Azure platform.

Ricardo Villalobos is a seasoned software architect with more than 15 years of experience designing and creating applications for companies in the supply chain management industry. Holding different technical certifications, as well as a master’s degree in business administration from the University of Dallas, he works as a cloud architect in the Azure CSV incubation group for Microsoft.

Thanks to the following technical expert for reviewing this article: Glenn Block
Glenn Block works on the Azure team making sure it's a kick ass platform for open source development. When he's not developing products or with family, you'll find him at a conference somewhere in the world, hacking away on some new thing, pairing up with whoever he can find, or tweeting into the wee hours of the night as @gblock.