Microservices using Rails, HTTP & RabbitMQ

Sep 19, 2016

Imagine that, we have built one monolithic app Cricket Live Score where an
admin adds the live match scores into the database, and users use this app to get
to know the current score about the live match. Match like India v Pakistan
is very popular and it tends to have lots of traffic. Sometimes app goes down
because it can’t handle such heavy traffic.

Problem:

If you see here, there are two problems,

To get the current score, the user hits the page several times which causes
lots of incoming requests to the server and due to this app server goes down.

For a live match, if the server is down due to heavy load, then admin is not
able to do anything. He sits quietly.

It’s not a good reason for admin for sitting quietly because of heavy load on
the server. We have to handle admin part in a way that there is no effect of
users activities on it. There should not be a relation between them. Getting
current score by reloading the page is also not a right way if the app has such
traffic.

Solution:

There are lots of solutions out there to handle this situation. But let’s see how
microservices sometimes play a crucial role in managing this situation.

Based on above two problems, let’s create two microservices (i.e. two Rails apps).
In one microservice, we are going to handle only admin part (i.e. backend), and in
another microservice, we will handle user part (i.e. frontend). Building them
separately, we can scale them independently like handling more effective caching
in frontend microservice.

NOTE: For frontend microservice, we can use other light-weight technologies
too like Sinatra instead of Rails.

Assumption:

In backend microservice, assume that we have added all required model, controller and
views files with code for match resource i.e. all the necessary business logic.
Admin user will come, add a new match and update related scores for the live
match. In frontend microservice, assume that we also have added a required
controller & views so that user can see the list of matches and can select any
one of them to see live scores of the match.

Communication:

To speak within these microservices, we need a communication layer. For that,
either we can use HTTP or RabbitMQ (AMQP Protocol). But in this situation,
we are going to use both. Why both? Because, in frontend microservice, we need
data for matches and we will fetch it from backend microservice using HTTP
request. In backend service, when admin updates any match’s live score, we also
need to update our users of frontend microservice in real-time. So users don’t
need to refresh the page to get the current score of the match. For that,
we are going to use RabbitMQ.

What is RabbitMQ?

RabbitMQ is Open Source messaging system sponsored by VMware.

RabbitMQ is a message broker. The principal idea is pretty simple: it accepts and forwards messages. You can think about it as a post office: when you send mail to the post box you’re pretty sure that Mr. Postman will eventually deliver the mail to your recipient. Using this metaphor RabbitMQ is a post box, a post office and a postman.

To access RabbitMQ from the browser we need this plugin RabbitMQ Web-Stomp
enabled.

RabbitMQ Web-Stomp plugin takes the STOMP protocol and
exposes it using either plain WebSockets or
a SockJS server.

Using these tools, we will push messages in real-time from RabbitMQ to
the web clients.

Using Bunny Ruby client, we connect to RabbitMQ server on local machine. By
adding proper queue name and other settings, we send the match data to RabbitMQ
server. To know more about these settings/configurations, I would suggest following
this tutorial on
RabbitMQ site.

In index action of MatchesController, we fetch matches list like @live_matches,
and @previous_matches from backend microservice and then we show them to users.
The user will pick any live match to get the real-time match updates. In show
action we fetch match’s recent data then the user will start getting real-time
scores automatically if the match is live.

To get the real-time updates in show.html.erb page, we need to make a
connection to RabbitMQ server. Let’s do it.

If you see in above code, Stomp JavaScript client communicates to a STOMP
server over WebSocket. Once a STOMP client is created, it should call its
connect() to connect and authenticate to the STOMP server. In connect()
method we have passed default parameters login & passcode and those are
mandatory parameters. We also have passed callbacks so that we can fetch
the message from RabbitMQ server. You can read more about this flow in this
article.

In onConnect method, we subscribe to queue and through which we get the
real-time message from RabbitMQ server if there is any. Once we get the match data
message, then we can handle our logic and show the match data to users in
the proper format.

This blog post is all about how we can decouple the app into separate components
using microservices which help to solve above two problems.