Scaling Node.js
applications with

microservices

Armagan Amcalar

FullStack 2015, London
Oct 25th, 2015

who am i?

Armagan Amcalar
Head of Software Engineering @ unu GmbH
Founder @ Lonca Works

        dashersw

hobby projects

pedals.io
trax.io

Node.js

We love it.

We develop all kinds of software with it.

Programming model encourages stateful app design.

We love to daemonize it, all apps are running as daemons.

the problem

It doesn't scale well.

 

Works great as single process.

Works OK as a cluster.

what's next?

What if you need more computing power?

How about horizontal scalability?

First solution

nginx

node-http-proxy

REVERSE PROXY

we need a different solution.

Because we have more problems

We're not just building express apps

Where's the flexibility and agility in that?

more problems?

Be fast with your API response

Asynchronous processing (e.g., analytics)

You may need to scale very dynamically

High availability (fault tolerance)

IT WAS BTTF DAY A FEW DAYS AGO,
WHERE ARE MODULAR, COMPOSABLE DESIGN AND EVENTS?

Two approaches To Architecture

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

Problems of microservices

Communication between services

Transactional processes

Dynamic service configuration

Service discovery

Health checks & monitoring

can nginx help?

Somewhat!

 

Use as service registry

Update config on any change in services

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

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, fanouts, sinks, 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
        });
    });
});

enter cote.js

node.js framework for building fault-tolerant,
distributed applications

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);
});

case study
E-Commerce application

Payments processor service

Products catalog service

Purchasing service

User and accounts service

 

https://github.com/dashersw/cote-workshop

Conclusion

With RabbitMQ or cote.js we can;

 

Apply sophisticated programming paradigms

Scale nearly infinitely

Have fault-tolerant systems

thank you!

Join cote.js community

Let's keep in touch!

Armagan Amcalar

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

Scaling Node.js applications with microservices

By Armağan Amcalar

Scaling Node.js applications with microservices

In this talk, Armagan explores different scaling strategies for Node.js applications like nginx and RabbitMQ, and observe their advantages and disadvantages. The last strategy will be a microservices solution via a Node.js framework, cote.js. You will learn how to implement an optimized, zero-conf, fault-tolerant, distributed scaling solution that plays extremely well with the microservices architecture.

  • 3,831