In this article, I will discuss sending messages from server to web browser using RabbitMQ-Web-Stomp as the backend, and a simple JavaScript library to handle the web browser side. SockJS is used as the browser WebSockets library. This setup enables you to directly push messages in realtime from the RabbitMQ message broker to the web client. This makes it possible to deliver status updates to multiple web clients directly from the message broker. No need for special application server software in the middle.
The reverse is also possible. You can push messages from web browsers directly into the RabbitMQ routing system. This article, though, will discuss the former case, from server to client. The infrastructure required for the latter case is the same, with nothing extra needed.
Table of Contents
What’s Needed
RabbitMQ is a piece of server software which implements the AMQP messaging standard. Simply put, it maintains a number of queues for holding incoming messages, and enables other software to publish messages to those queues, and fetch messages from the queues. There are API libraries for all major programming languages, including C, Python, Java, Ruby, PHP and, of course, Erlang, because RabbitMQ itself is written in it.
AMQP is an open standard for messaging middleware. In theory, AMQP products from various vendors should be compatible. AMQP is a binary line protocol, which makes it fast, but unsuitable for web apps.
STOMP is the Simple (or Streaming) Text Orientated Messaging Protocol. The RabbitMQ-Web-Stomp plugin can deliver messages to/from web clients using Stomp.
The Stomp over Web Socket library allows running STOMP streams over web sockets.
SockJS is a WebSocket emulation library which gives a consistent API on all browser platforms, including some older ones which have partial or vendor-specific support. Native web sockets are used if the browser supports them.
RabbitMQ Installation
Here’s the RabbitMQ download page. The stuff discussed here has been tested with the package version 3.0.2 on Ubuntu, which is not yet in the official repositories.
After installation, you can use the rabbitmqctl command to command the server. It will require root privileges.
Web Management Plugin and the rabbitmqadmin script
After installing the package, enable the management plugin:
1 |
sudo rabbitmq-plugins enable rabbitmq_management |
And restart:
1 |
sudo /etc/init.d/rabbitmq-server restart |
The management plugin provides a web interface for configuring the server. By default it runs at port 15762 (eg. http://localhost:15672/).
The default username is “guest” and the password is “guest”. You should obviously change these. It can be done using the web admin interface.
You can download the rabbitmqadmin Python script from http://localhost:15672/cli/ . You can control RabbitMQ with this script without root privileges, if you provide a username and password either on the command line or in ~/.rabbitmqadmin.conf (run “rabbitmqdamin help configuration” for instructions).
Writing Programs with AMQP
Here are some very nice tutorials on how to write programs for RabbitMQ:
http://www.rabbitmq.com/getstarted.html
We will be using the same pattern as example number 5. But we will use a web client as the consumer.
Messaging in a Web Browser using RabbitMQ-Web-Stomp
The RabbitMQ-Web-Stomp plugin allows web browsers to do messaging using SockJS. This will enable a web browser to keep a connection with the message broker, and receive messages as soon as they appear in the queue. A callback function will be fired every time upon the receival of a message.
Install the Web Stomp plugin:
1 |
sudo rabbitmq-plugins enable rabbitmq_web_stomp |
Restart RabbitMQ. You should now get a hello page in port 15674 (note the different port number), under /stomp (http://localhost:15674/stomp).
(If you install the rabbitmq_web_stomp_examples plugin as well, you can find examples at http://localhost:15670/. You can download the stomp.js file used in my example from http://localhost:15670/web-stomp-examples/stomp.js)
A simple example
To demonstrate a simple use case, let’s write two programs. The first one is a Python program for publishing messages, and the other one is a web app for reading those messages.
The publisher in Python
This one will use the Pika AMQP client library (sudo apt-get install python-pika, or pip install pika).
Let’s call this app publisher.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#!/usr/bin/python import pika # Use plain credentials for authentication mq_creds = pika.PlainCredentials( username = "guest", password = "guest") # Use localhost mq_params = pika.ConnectionParameters( host = "localhost", credentials = mq_creds, virtual_host = "/") # Anyone subscribing to topic "mymessages" receives our messages mq_exchange = "amq.topic" mq_routing_key = "mymessages" # This a connection object mq_conn = pika.BlockingConnection(mq_params) # This is one channel inside the connection mq_chan = mq_conn.channel() import readline print("Press Ctrl+C to quit.\n") while True: text = raw_input("Enter your message: ") print("Sending '" + text + "'") mq_chan.basic_publish( exchange = mq_exchange, routing_key = mq_routing_key, body = text) |
That will simply prompt the user for a line of text, and publish it to the amq.topic exchange of the default virtualhost “/”, using routing key mymessages.
Run the script with:
1 |
python publisher.py |
The Consumer in JavaScript
Here’s an example app which can read messages using SockJS. Let’s call it listener-app.html.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Testing RabbitMQ Web Stomp</title> <!-- From: http://cdn.sockjs.org/ --> <script src="sockjs-0.3.js"></script> <!-- From RabbitMQ-Web-Stomp examples --> <script src="stomp.js"></script> <!-- Our example app --> <script src="listener-app.js"></script> <style> #output { border: 1px solid black; min-height: 100px; } </style> </head> <body> <h1>Waiting for messages</h1> <div id="output"> <!-- incoming messages will be printed here --> </div> </script> </body> </html> |
Here’s listener-app.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// Use SockJS Stomp.WebSocketClass = SockJS; // Connection parameters var mq_username = "guest", mq_password = "guest", mq_vhost = "/", mq_url = 'http://' + window.location.hostname + ':15674/stomp', // The queue we will read. The /topic/ queues are temporary // queues that will be created when the client connects, and // removed when the client disconnects. They will receive // all messages published in the "amq.topic" exchange, with the // given routing key, in this case "mymessages" mq_queue = "/topic/mymessages"; // This is where we print incomoing messages var output; // This will be called upon successful connection function on_connect() { output.innerHTML += 'Connected to RabbitMQ-Web-Stomp<br />'; console.log(client); client.subscribe(mq_queue, on_message); } // This will be called upon a connection error function on_connect_error() { output.innerHTML += 'Connection failed!<br />'; } // This will be called upon arrival of a message function on_message(m) { console.log('message received'); console.log(m); output.innerHTML += m.body + '<br />'; } // Create a client var client = Stomp.client(mq_url); window.onload = function () { // Fetch output panel output = document.getElementById("output"); // Connect client.connect( mq_username, mq_password, on_connect, on_connect_error, mq_vhost ); } |
You will also need the sockjs-0.3.js and stomp.js files. I will include them in this post.
Test It
If you now point your browser to http://localhost/rabbitmq-web-stomp-example/listener-app.html, you should see a text saying “Connected to RabbitMQ-Web-Stomp”.
Now, write a message in the terminal window running the publish.py Python script. It should appear in the browser window immediately.
You can open multiple browser windows, and they should all get the message. You can also open multiple publisher scripts at once.
At this point, you should see some connections, channels and queues appear in the RabbitMQ web management interface. There should be one of each for every browser window and running Python script.
Useful links:
- http://www.rabbitmq.com/blog/2012/05/14/introducing-rabbitmq-web-stomp/
- http://www.rabbitmq.com/web-stomp.html
- http://www.rabbitmq.com/stomp.html
- https://github.com/sockjs/sockjs-client
- http://jmesnil.net/stomp-websocket/doc/
Here’s the example, unpack it under you web server’s document root:
Thanks a ton!
https://raw.github.com/rabbitmq/rabbitmq-web-stomp-examples/master/priv/stomp.js
That is the link to Stomp.js.
How to implement this when connection over SSL
I’ve been trying to use this example, and I wasn’t getting javascript to connect, I finally did it using Websocket instead of SockJs. So if there is someone wondering by with same problem, here is what I did:
Erase this lines:
2 |Stomp.WebSocketClass = SockJS;
40 | var client = Stomp.client(mq_url);
Add this in the beggining:
var ws = new WebSocket(‘ws://127.0.0.1:15674/ws’);
var client = Stomp.over(ws);
And it should work!
Awesome Post.. You web sample work like charm !!!!!
excellent !! many many thanks!! i’m using a different vhost (i call it “ursa”) and a publish/suscribe pattern with an exchange of type fanout called “status”, the code is the next:
var Stomp = window.Stomp; // i’m using requirejs, but can’t import Stomp
var mq_username = “another_user_name”;
var mq_password = “my_pass”;
var mq_vhost = “ursa”;
var mq_queue = “/exchange/status”;
var ws = new WebSocket(‘ws://’+location.host+’:15674/ws’);
var client = Stomp.over(ws);