Scaling Node.js
applications with

Redis, RabbitMQ & cote.js

Armagan Amcalar

JSIST 2014, Istanbul
Sep 27th, 2014

who am i?

Armagan Amcalar
Co-founder, Chief Software Architect
Startup Kitchen

        dashersw

hobby projects

pedals.io
trax.io

Node.js

We love it.

We develop all kinds of software with it.

We love to daemonize it.

the problem

It doesn't scale well.

 

Programming model encourages stateful app design

Works great as single process

Works OK as a cluster

what's next?

What if you need more computing power?

How about horizontal scalability?

Current solution

nginx

node-http-proxy

REVERSE PROXY

nginx

Easy to set up if you're familiar with nginx

Hard to maintain

Yet another technology dependency

node-http-proxy

Hard to set up even if you're familiar

Hard to maintain

Very low level

we need a different solution.

Because we have more problems

We're not just building express apps

We were programming the DOM, we need that flexibility

more problems?

Be fast with your API response

Asynchronous processing (e.g., analytics)

You may need to scale very dynamically

Fault tolerance

THE YEAR'S 2014, WHERE ARE THE EVENTS?

Let's take a break.

monolithic apps
vs
service oriented architecture & microservices

monolithic applications

Single code base
One process to rule them all
A bug anywhere crashes the whole system
May consume lots of stale resources
Hard to scale; needs even more stale resources

Request–response flow is one huge piece

 

Easy to manage

Microservices

Separate code base
Multi processes for each service
A bug in a service crashes only that service
Consumes minimal resources
Scales well, needs minimal additional resources

Supports event-driven architecture

Request–response flow breakdown

 

Hard to manage

event-driven architecture

pub-sub pattern

observer pattern

pub-sub pattern

Just like DOM events, what we're used to build.
Instead of click events, you have custom events (e.g. in jQuery)
Instead of DOM element interaction, you have daemon interaction

observer pattern

First-of-its-kind event driven pattern
You observe an object property, not an arbitrary event
Like all your click handlers have to have the same name
Limits possibilities

enter redis

Originally a NoSQL database
Key-value pair server with lots of great features like sets, etc.

 

Pub-sub capability
Supports events based on channels and patterns
Very cool node.js library

a redis pubsub-based architecture

redis

d1

d4

d3

d2

publishes foo

notifies of foo

notifies of foo

notifies of foo

client request

session storage

logs

API

example

var redis = require('redis'),
    client = redis.createClient(6379, '127.0.0.1');

client.publish('request', JSON.stringify({
    body: 'some cool request'
}));
var redis = require('redis'),
    logger = redis.createClient(6379, '127.0.0.1');

logger.subscribe('request');

logger.on('message', function(channel, message) {
    if (channel == 'request')
        console.log(JSON.parse(message));
});

Client

Logger

quick demo

enter rabbitmq

A message broker for distributed applications
Like redis pubsub, with heavy focus on additional features
such as very flexible message routing

 

work queues, topics, etc.
QoS management
round robin load balancing
message acks
rate limiting
 

unfriendly node.js library

work queues

remote procedure calls

example work queue

var amqp = require('amqp'),
    connection = amqp.createConnection(),
    workId = 0;

connection.on('ready', function() {
    connection.queue('work_queue', {autoDelete: false, durable: true}, function(queue) {
        setInterval(function() {
            connection.publish('work_queue', 'message #' + ++workId, { deliveryMode: 2 });
            console.log('sent message #' + workId);
        }, 1000);
    });
});
var amqp = require('amqp'),
    connection = amqp.createConnection();

connection.on('ready', function () {
    connection.queue('work_queue', {autoDelete: false, durable: true}, function(queue) {
        queue.subscribe({ack: true, prefetchCount: 1}, function(msg) {
            var body = msg.data.toString('utf-8');
            console.log("received", body);
            queue.shift(); // basic_ack equivalent
        });
    });
});

quick demo

enter cote.js

node.js framework for fault tolerant, distributed apps
developed at Startup Kitchen
partially funded by TUBITAK

 

 

cote.js features

auto-discovery
mesh network, peer-to-peer communication
pubsub pattern
requester/responder pattern
client-side communication with websockets
load balancing with different strategies
daemon monitor

example architecture

example pub-sub implementation

var Publisher = require('cote').Publisher,
    publisher = new Publisher({
        name: 'publisher',
        broadcasts: ['update']
    }),
    workId = 0;

publisher.on('ready', function() {
    setInterval(function() {
        console.log('emitting', ++workId);
        publisher.publish('update', workId);
    }, 3000);
});
var Subscriber = require('cote').Subscriber,
    subscriber = new Subscriber({
        name: 'subscriber'
    });

subscriber.on('update', function(message) {
    console.log('update', message);
});

quick demo

Conclusion

With Redis, RabbitMQ or cote.js we can;

 

Apply sophisticated programming paradigms

Scale nearly infinitely

Have fault-tolerant systems

thank you!

contribute to cote.js

Let's keep in touch!

Armagan Amcalar

armagan@amcalar.com
twitter: @aamcalar
github: dashersw
blog: arm.ag

Scaling Node.js applications with Redis, RabbitMQ and cote.js

By Armağan Amcalar

Scaling Node.js applications with Redis, RabbitMQ and cote.js

Node.js has proven itself as a very useful environment for building network applications. Yet, JavaScript being single-threaded, a problem remains on scaling your application horizontally to accommodate client requests. In this talk, we’ll explore multi process scaling with Redis pub-sub, a message queuing topology with RabbitMQ, and a step further to fault tolerant microservices with cote.js.

  • 9,947