Cloud Foundry Blog

Scaling Real-time Apps on Cloud Foundry Using Node.js and RabbitMQ

In the previous blog Scaling Real-time Apps on Cloud Foundry Using Node.js and Redis, we used Redis as a ‘session store’ and also as a ‘pub-sub’ service for chat messages. But in many enterprise grade real-time apps, you may want to use RabbitMQ instead of Redis to do pub-sub because of the reliability and features that comes out-of-the-box in RabbitMQ. This is especially true for financial or Bank apps like Stock Quote apps where it is critical to protect and deliver each-and-every message AND do it as quickly as possible.

So, in this blog, we will start from Scaling Real-time Apps on Cloud Foundry Using Node.js and Redis and simply replace Redis with RabbitMQ pubsub.

The app architecture (before):

The app architecture w/ RabbitMQ (after):


Introduction to RabbitMQ

The Node.js community may not be familiar with RabbitMQ. So here are some of the high-level intro of RabbitMQ.

RabbitMQ is a message broker. It simply accepts messages from one or more endpoints “Producers” and sends it to one or more endpoints “Consumers”.

RabbitMQ is more sophisticated and flexible than just that. Depending on the configuration, it can also figure out what needs to be done when a consumer crashes(store and re-deliver message), when consumer is slow (queue messages), when there are multiple consumers (distribute work load), or even when RabbitMQ itself crashes (durable). For more please see: RabbitMQ tutorials.

RabbitMQ is also very fast & efficient. It implements Advanced Message Queuing Protocol “AMQP” that was built by and for Wall Street firms like J.P. Morgan Chase, Goldman Sachs, etc. for trading stocks and related activities. RabbitMQ is an Erlang (also well-known for concurrency & speed) implementation of that protocol.

For more please go through RabbitMQ’s website.


Fundamental pieces of RabbitMQ

RabbitMQ has 4 pieces.

  1. Producer (“P”) – Sends messages to an exchange along with “Routing key” indicating how to route the message.
  2. Exchange (“X”) – Receives message and Routing key from Producers and figures out what to do with the message.
  3. Queues(“Q”) – A temporary place where the messages are stored based on Queue’s “binding key” until a consumer is ready to receive the message. Note: While a Queue physically resides inside RabbitMQ, a consumer (“C”) is the one that actually creates it by providing a “Binding Key”.
  4. Consumer(“C”) – Subscribes to a Queue to receive messages.

Routing Key, Binding Key and types of Exchanges

To allow various work-flows like pub-sub, work queues, topics, RPC etc., RabbitMQ allows us to independently configure the type of the Exchange, Routing Key and Binding Key.

Routing Key:

A string/constraint from Producer instructing Exchange how to route the message. A Routing key looks like: “logs”, “errors.logs”, “warnings.logs” “tweets” etc.

Binding Key:

Another string/constraint added by a Consumer to a queue to which it is binding/listening to. A Binding key looks like: “logs”, “*.logs”, “#.logs” etc.

Note: In RabbitMQ, Binding keys can have “patterns” (but not Routing keys).

Types of Exchange:

Exchanges can be of 4 types:

  1. Direct – Sends messages from producer to consumer if Routing Key and Binding key match exactly.
  2. Fanout – Sends any message from a producer to ALL consumers (i.e ignores both routing key & binding key)
  3. Topic – Sends a message from producer to consumer based on pattern-matching.
  4. Headers – If more complicated routing is required beyond simple Routing key string, you can use headers exchange.

In RabbitMQ the combination of the type of Exchange, Routing Key and Binding Key make it behave completely differently. For example: A fanout Exchange ignores Routing Key and Binding Key and sends messages to all queues. A Topic Exchange sends a copy of a message to zero, one or more consumers based on RabbitMQ patterns (#, *).

Going into more details is beyond the scope of this blog, but here is another good blog that goes into more details: AMQP 0-9-1 Model Explained


Using RabbitMQ to do pub-sub in our Node.js chat app.

Now that we know some of the basics of RabbitMQ, and all the 4 pieces, let’s see how to actually use it in our Chat app.

Chat App:

Connecting to RabbitMQ and creating an Exchange

For our chat application, we will create a fanout exchange called chatExchange. And we will be using node-amqp module to talk to RabbitMQ service.

//Connect to RabbitMQ and get reference to the connection.
var rabbitConn = amqp.createConnection({});

//Create an exchange with a name 'chatExchange' and of type 'fanout'
var chatExchange;
rabbitConn.on('ready', function () {
    chatExchange = rabbitConn.exchange('chatExchange', {'type': 'fanout'});
});

Creating Producers (So Users can send chat messages)

In our chat app, users are both producers(i.e. sends chat messages to others) and also consumers (i.e. receives messages from others). Let’s focus on users being ‘producers’.

When a user sends a chat message, publish it to chatExchange w/o a Routing Key (Routing Key doesn’t matter because chatExchange is a ‘fanout’).

/**
     * When a user sends a chat message, publish it to chatExchange w/o a Routing Key (Routing Key doesn't matter
     * because chatExchange is a 'fanout').
     *
     * Notice that we are getting user's name from session.
     */
    socket.on('chat', function (data) {
        var msg = JSON.parse(data);
        var reply = {action: 'message', user: session.user, msg: msg.msg };
        chatExchange.publish('', reply);
    });

Similarly, when a user joins, publish it to chatExchange w/o Routing key.

/**
     * When a user joins, publish it to chatExchange w/o Routing key (Routing doesn't matter
     * because chatExchange is a 'fanout').
     *
     * Note: that we are getting user's name from session.
     */
    socket.on('join', function () {
        var reply = {action: 'control', user: session.user, msg: ' joined the channel' };
        chatExchange.publish('', reply);
    });

Creating Consumers (So Users can receive chat messages)

Creating consumers involves 3 steps:

  1. Create a queue with some options.
  2. Bind queue to exchange using some “Binding Key”
  3. Create a subscriber (usually a callback function) to actually obtain messages sent to the queue.

For our chat app,

  1. Let’s create a queue w/o any name. This forces RabbitMQ to create new queue for every socket.io connection w/ a new random queue name. Let’s also set exclusive flag to ensure only this consumer can access the messages from this queue.
rabbitConn.queue('', {exclusive: true}, function (q) {
 ..
 }
  1. Then bind the queue to chatExchange with an empty ‘Binding key’ and listen to ALL messages.
q.bind('chatExchange', "");
  1. Lastly, create a consumer (via q.subscribe) that waits for messages from RabbitMQ. And when a message comes, send it to the browser.
q.subscribe(function (message) {
   //When a message comes, send it back to browser
   socket.emit('chat', JSON.stringify(message));
 });

Putting it all together.

sessionSockets.on('connection', function (err, socket, session) {
    /**
     * When a user sends a chat message, publish it to chatExchange w/o a Routing Key (Routing Key doesn't matter
     * because chatExchange is a 'fanout').
     *
     * Notice that we are getting user's name from session.
     */
    socket.on('chat', function (data) {
        var msg = JSON.parse(data);
        var reply = {action: 'message', user: session.user, msg: msg.msg };
        chatExchange.publish('', reply);
    });

   /**
     * When a user joins, publish it to chatExchange w/o Routing key (Routing doesn't matter
     * because chatExchange is a 'fanout').
     *
     * Note: that we are getting user's name from session.
     */
    socket.on('join', function () {
        var reply = {action: 'control', user: session.user, msg: ' joined the channel' };
        chatExchange.publish('', reply);
    });


   /**
     * Initialize subscriber queue.
     * 1. First create a queue w/o any name. This forces RabbitMQ to create new queue for every socket.io connection w/ a new random queue name.
     * 2. Then bind the queue to chatExchange  w/ "#" or "" 'Binding key' and listen to ALL messages
     * 3. Lastly, create a consumer (via .subscribe) that waits for messages from RabbitMQ. And when
     * a message comes, send it to the browser.
     *
     * Note: we are creating this w/in sessionSockets.on('connection'..) to create NEW queue for every connection
   */
    rabbitConn.queue('', {exclusive: true}, function (q) {
        //Bind to chatExchange w/ "#" or "" binding key to listen to all messages.
        q.bind('chatExchange', "");

   //Subscribe When a message comes, send it back to browser
        q.subscribe(function (message) {
            socket.emit('chat', JSON.stringify(message));
        });
    });
 });

Running / Testing it on Cloud Foundry

  • Clone this app to rabbitpubsub folder
  • cd rabbitpubsub
  • npm install & follow the below instructions to push the app to Cloud Foundry
[~/success/git/rabbitpubsub]
> vmc push rabbitpubsub
Instances> 4       <----- Run 4 instances of the server

1: node
2: other
Framework> node

1: node
2: node06
3: node08
4: other
Runtime> 3  <---- Choose Node.js 0.8v

1: 64M
2: 128M
3: 256M
4: 512M
Memory Limit> 64M

Creating rabbitpubsub... OK

1: rabbitpubsub.cloudfoundry.com
2: none
URL> rabbitpubsub.cloudfoundry.com  <--- URL of the app (choose something unique)

Updating rabbitpubsub... OK

Create services for application?> y

1: blob 0.51
2: mongodb 2.0
3: mysql 5.1
4: postgresql 9.0
5: rabbitmq 2.4
6: redis 2.6
7: redis 2.4
8: redis 2.2
What kind?> 5 <----- Select & Add RabbitMQ 2.4v service (for pub-sub)

Name?> rabbit-e1223 <-- This is just a random name for RabbitMQ service

Creating service rabbit-e1223... OK
Binding rabbit-e1223 to rabbitpubsub... OK

Create another service?> y

1: blob 0.51
2: mongodb 2.0
3: mysql 5.1
4: postgresql 9.0
5: rabbitmq 2.4
6: redis 2.6
7: redis 2.4
8: redis 2.2
What kind?> 6 <----- Select & Add Redis 2.6v service (for session store)

Name?> redis-e9771 <-- This is just a random name for Redis service

Creating service redis-e9771... OK
Binding redis-e9771 to rabbitpubsub... OK

Bind other services to application?> n

Save configuration?> n

Uploading rabbitpubsub... OK
Starting rabbitpubsub... OK
Checking rabbitpubsub... OK

  • Once the server is up, open up multiple browsers and go to <servername>.cloudfoundry.com
  • Start chatting.

Tests

Test 1

  • While chatting, refresh the browser.
  • You should automatically be logged in.

Test 2

  • Open up JS debugger (On Chrome, do cmd + alt +j )
  • Restart the server by doing vmc restart <appname>
  • Once the server restarts, Socket.io should automatically reconnect
  • You should be able to chat after the reconnection.

That’s it for now. Hopefully this blog helps you get started with using RabbitMQ. Look forward for more Node.js and RabbitMQ related blogs. The content of this blog has also been covered in a video. Feel free to get in touch with us for questions on the material.


General Notes

  • Get the code right away – Github location: https://github.com/rajaraodv/rabbitpubsub.
  • Deploy right away – if you don’t already have a Cloud Foundry account, sign up for it here.
  • Check out Cloud Foundry getting started here and install the vmc Ruby command line tool to push apps.
  • To install the latest alpha or beta vmc tool run: sudo gem install vmc --pre.
Facebook Twitter Linkedin Digg Delicious Reddit Stumbleupon Email

New Runtime Module for Node.js Applications

In the previous blog post, Cloud Foundry Now Supports Auto-Reconfiguration for Node.js Applications, we saw that Node.js applications deployed to CloudFoundry.com can be automatically reconfigured to connect to Cloud Foundry services. However, there may be situations where you want to opt-out of that feature to have finer control over service connections or to overcome its limitations. In those cases, your applications would be required to parse JSON-formatted environment variables to perform that same function. While this is not overly complex given that JSON is trivial to parse with JavaScript, you will be required to understand the environment variable names and their payload schema. The new cf-runtime module introduced in this blog simplifies this by providing a way to obtain application information and service connection objects. This module moves Cloud Foundry’s Node.js support forward to the match the support for Java and Ruby applications.

Installation

Cf-runtime is available in the npm registry and can be easily installed with the Node Package Manager (npm). Run the following command in the base directory of your Node.js application.

npm install cf-runtime

Usage

This node module provides access to two types of objects. The first is a preconfigured object named CloudApp that contains application information. This includes the application’s host and port configured by Cloud Foundry, list of services bound to the application and their properties.

Additionally, each service that is bound to the application can be accessed via <ServiceName>Client object (i.e. RedisClient, MysqlClient). This object provides a convenient way to obtain the corresponding service connection with just a single function call. You can either create a service connection by the name used to create the service instance or by providing a general service name (e.g. redis or mongo) if there is only one service of this type that is bound to your application. This function may also accept additional parameters depending on the node module it uses (see details below in Service Clients section).

var cf = require('cf-runtime')
var app = cf.CloudApp

// Check if application is running in Cloud Foundry

app.runningInCloud

// Get application properties

app.host
app.port

// Get the list of application service names

app.serviceNames

// Obtain connection properties for single service of type Redis

app.serviceProps.redis

// Obtain connection properties for service named 'redis-service-name'

app.serviceProps['redis-service-name']

// Obtain the list of service names of specific type

app.serviceNamesOfType.redis

// Check if service of the given type is available

cf.RedisClient !== undefined

// Connect to a single service of type Redis

var redisClient = cf.RedisClient.create()

// Connect to redis service named 'redis-service-name'

var redisClient = cf.RedisClient.createFromSvc('redis-service-name')

Service Properties

All services have the following common properties:

  • name: specific name of the service
  • label: name of service type, for example “redis”, “mysql”
  • version: software version of the service type
  • host
  • port
  • username
  • password
  • url: service connection url

Additionally, PostgreSQL, MySQL and Redis include this service property:

  • database: the name of the database that is provided by the service

RabbitMQ provides access to these additional properties:

  • vhost: the name of the virtual host

MongoDB provides access to these additional properties:

  • db: the database name

Service Clients

The following table shows the available methods and parameters for each service type:

Node module Returns Functions Parameters
MongoDB
mongodb null cf.MongoClient.create([options], callback)
cf.MongoClient.createFromSvc(name, [options], callback)
name- the name of a service bound to the appoptions- optional {object} non-connection related optionscallback – {function} connection callback
MySQL
mysql Mysql client instance cf.MysqlClient.create([options])
cf.MysqlClient.createFromSvc(name, [options])
name- the name of a service bound to the appoptions – optional {object} non-connection related options
PostgreSQL
pg {boolean} cf.PGClient.create(callback)
cf.PGClient.createFromSvc(name, callback)
name- the name of a service bound to the appcallback – {function} connection callback
RabbitMQ
amqp AMQP client instance cf.AMQPClient.create([implOptions])
cf.AMQPClient.createFromSvc(name, [implOptions])
name- the name of a service bound to the appimplOptions – optional {object} non-connection related implementation options
Redis
redis Redis client instance cf.RedisClient.create([options])
cf.RedisClient.createFromSvc(name, [options])
name- the name of a service bound to the appoptions – optional {object} non-connection related options

Summary

The main purpose of cf-runtime is to make your Node.js applications understand their cloud better, retrieve the environment properties, find the available services, and connect to them easily. If you are writing Node.js applications, cf-runtime just made it easier to deploy your applications to Cloud Foundry.

- Maria Shaldibina
The Cloud Foundry Team

Don’t have a Cloud Foundry account yet?  Sign up for free today

Facebook Twitter Linkedin Digg Delicious Reddit Stumbleupon Email

RabbitMQ + Cloud Foundry: Cloud Messaging that Just Works

Cloud Foundry™ is the industry’s first open platform as a service. Providing a choice of developer frameworks, application services and deployment clouds, Cloud Foundry simplifies application development and makes it faster and easier to build, test, deploy and scale applications. Cloud Foundry already supports multiple application services including MySQL, MongoDB and Redis. And we are working to add more. 

Today, we are pleased to announce an important milestone: the RabbitMQ messaging service, available today as a free public beta. You can get started right away: on CloudFoundry.com, you will find sample applications and guides. If you create a cool application, please let us know and we’ll link to it. And if you get stuck, tell us what we need to get right. 

Our goal is to make it as easy as possible for you to use messaging to create and connect to cloud applications. This radically simpler portability of applications and integration of services is the essence of Cloud Foundry. To understand what this means for messaging as a service, let’s look at how people use RabbitMQ in the cloud and then see why we think offering RabbitMQ as a service makes sense.

Messaging for the cloud

In the cloud, communication is the design center. 

Messaging is fundamentally a communication technology and therefore a cloud enabler. People talk about “event streams” and “task queues” but the core pattern is “moving a lot of data reliably”. Cloud applications that involve multiple processes or VMs can benefit from a communication centric design. Take a look at this blog post from Union Station for example, and notice that their architecture is not a ‘stack’ but a ‘network’. Or have a look at Google’s design for their Rocksteady monitoring and analytics system. 

You can see this in any development meeting. Increasingly people who build applications ask “how do these components wire together?” and “what’s my event model?” where they used to ask “what’s my database schema and what’s my language platform”. This was always true for “enterprise integration” but now these patterns are needed even for new applications. The applications that are leading the way are also some of the biggest, e.g. near real-time data analytics and activity streams on global scale social and mobile enabled apps. The shift to these design patterns is driving adoption of streaming data services and event based technologies like messaging.

Messaging is essential for successful cloud applications for two reasons. 

First, messaging provides stable communication patterns that scale across multiple network topologies. In the cloud it is critical for applications to cope when networks change or grow. This means that the constituent components of cloud applications cannot be coupled directly. Instead, asynchronous decoupling and indirection based on message brokers is used. These two patterns enable work to be addressed, routed and delivered between components. Those components may be running in one cloud, or several, or on multiple devices e.g. phones. RabbitMQ has a proven track record of moving a lot of data reliably in these scenarios at scale.

Second, because those components are likely to be written in multiple programming languages, it is necessary that communication be “polyglot” and therefore “data centric”. Because of this, people look for a scalable and stable data fabric or communication backbone that can cope with a wide range of client languages, broadcast and load patterns. Messaging systems are designed to be robust with respect to arbitrary traffic conditions, e.g. reliably paging data to disk when consumers go offline. Again, RabbitMQ has the required breadth to deliver on this requirement.

Here are some benefits for cloud applications:

  1. Maintainability: messaging lets you more easily maintain your application without impacting uptime. Decoupling components reduces complexity and simplifies integration. Moreover logging, alerts, reporting and management benefit from a messaging based design.
  2. Scalability: you can improve responsiveness in applications. For instance, messaging lets you separate factor time-consuming processing tasks out of web-facing applications and into a separate tier of worker instances. In addition you can add more of these workers and back end services to achieve scale.
  3. Efficiency: messaging pushes data to users, devices and applications, which is much more efficient than using a database to share information between applications and continually querying for changes. In the cloud, polling is aggravated by variable latency. Pushing data when it changes is more effective.
  4. Robustness: in the cloud, not all components are connected or online at all times. Messaging lets you queue up data for subsequent consumption without fear of losing your data.

What’s next? Simple, Effective Cloud Messaging

Arguably, the success of RabbitMQ thus far has been due to providing a convenient and trustworthy way to get the benefits above, across lots of languages, and with a simple open source license. And certainly this is visible ‘in the cloud’ where RabbitMQ is a top choice both on the main clouds and running some of them under the hood too. One example is NASA’s Nebula cloud, but there are many more.

But there’s one problem – in the age of ‘IT as a service’, not everyone wants to install and look after their own RabbitMQ server. Wouldn’t it be great if management, maintenance, upgrades, rollbacks and all the other potential hindrances to scaling a business… just were taken care of by someone else? In 2011 all this seems obvious and logical to people across all kinds of infrastructure – including customers’ own hardware, behind their own security perimeter. 

The self-service experience is quite natural for messaging, and requests like “I just want a dev queue” or “spin me up ten instances of rabbit” should be part of the cloud. And that means part of the PaaS. So the first step is to provide RabbitMQ as a service for CloudFoundry.com. 

Get Started

Cloud Foundry.com makes it easy to begin using RabbitMQ. If you don’t have a CloudFoundry.com account yet, signup for free today.

In Cloud Foundry, the RabbitMQ server is a service on par with MySQL, MongoDB, and Redis. For instance, you use standard Cloud Foundry vmc commands to create, bind, unbind, clone, and delete RabbitMQ services. 

To learn more about using RabbitMQ on Cloud Foundry, you can use the following resources:

These applications should get you started but you can implement other patterns too. For example, you can use the RabbitMQ service to integrate distinct applications running on Cloud Foundry, even applications built using different languages and frameworks. Or perhaps you could wire RabbitMQ up with another service such as MongoDB. We want people to test the limit of what is possible so that we can stretch it.

The RabbitMQ service lets you implement a full set of essential cloud messaging patterns:

  • Work queues that distribute time-consuming tasks among multiple workers, in order to avoid doing a resource-intensive task immediately and having to wait for it to complete. Work queues let you schedule the task to be done later. This pattern is especially useful in web applications where it’s impossible to handle a complex task during a short HTTP request window. See for example the Celery project.
  • Publish/Subscribe to distribute messages to multiple consumers. This greatly simplifies integration and improves maintainability, since message producers and message consumers don’t need to know about each other. Publishers simply send their messages to an exchange (part of RabbitMQ server), which then looks at properties of the messages to distribute them to the appropriate queues, which are then read by one or more consumers.
  • Routing, to enables message consumers to subscribe only to a subset of the messages.
  • Topics, to do routing based on multiple criteria.
  • Remote Procedure Call (RPC), enabling a software application to run a function on a remote computer and wait for the result.

These core patterns are described in our online tutorials for RabbitMQ and in the Manning book “RabbitMQ in Action”.  For a short introduction to the RabbitMQ AMQP messaging model see here.

If you don’t already have a CloudFoundry.com account, signup here (it’s free).

Background, Pricing and Availability

The RabbitMQ service is now available on www.CloudFoundry.com as a free public beta. It is currently implemented using RabbitMQ server 2.4.1, and can be used by all languages and frameworks that are supported by Cloud Foundry and that have an AMQP 0-8 or 0-9-1 client available, including Ruby on Rails, Ruby Sinatra, Spring Java, and node.js.

RabbitMQ on Cloud Foundry is a cloud-based message broker. This is a type of server software that is used to integrate distinct software systems, by sending and receiving data (in the form of messages) between those systems. These systems can be components of the same application, or part of distinct applications, and can reside on the same machine (physical or virtual), on different machines in the same datacenter, or can be physically remote. 

RabbitMQ stands out among message brokers for several reasons. RabbitMQ scales and is extremely easy for developers to use. As the leading implementation of AMQP, an open-standard messaging protocol, RabbitMQ works with dozens of languages and frameworks that support the Advanced Message Queuing Protocol (AMQP), enabling developers to integrate applications built with completely different technologies, such as a Ruby application that needs to connect to a Java app. These capabilities – performance, usability, and ease of integration — have helped RabbitMQ become a leading choice, growing its popularity to the point where it is included as a standard message broker for major Linux distributions including Ubuntu, Fedora and Debian.

Please try the RabbitMQ service on Cloud Foundry and let us know how we can improve the experience by asking us questions or posting feature requests in our community forums.

Facebook Twitter Linkedin Digg Delicious Reddit Stumbleupon Email