Scalable Distributed System Design Principals

Do you ever wonder how software like Uber, Facebook, google are designed, I am not talking about initial design when they were designed for the first time, probably then they were designed with the simple monolithic approach but currently they are most reliable, scalable and very well-performing software.

Client-Server Model

Latency & Throughput

Availability

Caching

Proxy

const express = require('express');
const app = express();
app.listen(3000, () => console.tog('Listening on port 3000.'));
app.get('/hello', (req, res) => {
console.log(g.headers); res.send('Hello Adnan');
});
events { } 
http {
upstream nodejs-backend { server localhost:3000 }
server
{
listen 8081;
location / {
proxy_set_header system-designer-principals true;
proxy_pass http://nodejs-backend;
}
}
}

Load Balancer

express = require ('express') ; 
app = express();
port = process.env.PORT; I
app.listen(port, () => console.log('Listening on port ${port}.');
app.get('/hello', (req, res) => {
console.log(req.headers);
res.send('Hello from port ${port}.\n');
});
events { } 
http {
upstream nodejs-backend {
server localhost:3000 weight=3;
server localhost:3001
}
server { listen 8081;
location / {
proxy_set_header system-design-tutorial true;
proxy_pass http://nodejs-backend;
}
}
}

right Database

Replication & Sharding

Leader Election

Peer to Peer Networks

Polling and Streaming

const axios = require('axios'); 
const WebSocket = require('ws');
function createMessagingSocket()
{
return new WebSocket('ws://localhost:3001/messages1');
}
function getMessages() { return axios.get('http://localhost:3001/messages').then(res=> res.data);}
function sendMessage(message) { return axios.post('http://localhost:3001/messages', message); }
module.exports.createMessagingSocket = createMessagingSocket;
module.exports.getMessages = getMessages;
module.exports.sendMessage = sendMessage;
function getRandomInt(max)
{ return Math.floor( Math.random() * Math.floor(max)); }
module.exports.getRandomInt = getRandomInt;
const express = require('express'); const expressWs = requireCexpress-wsTh 
const app = express(); expressWs(app);
const messages = 0, text: 'Welcome!', username: 'Chat Room'}]; const sockets = [];
app.use(express.json());
app.listen(3001, () => { console.log('Listening on port 3001!'); });
app.get('/messages', (req, res) => {res.json(messages); });
app.post('/messages', (req, res) => {
const message = req.body; messages.push(message);
for (const socket of sockets)
{ socket.send(JSON.stringify(message)); }
});
app.ws('/messages', socket => {
sockets.push(socket);
socket.on('close', () => { sockets.splice(sockets.index0f(socket), 1);
});
});
const helpers = require('./helpers');
const messagingApi = require('./messaging_api');
const readline = require('readline');
const displayedMessages = {};
const terminal = readline.createInterface({ input: process.stdin, });
terminal.on('line', text => {
const username = process.env.NAME;
const id = helpers.getRandomInt(100000); displayedMessages[id] = true;
const message = {id, text, username}; messagingApi. sendMessage(message);
});
function displayMessage(message) {
console.log( '> ${message.username}: ${message.text' );
displayedMessages[message.id] = true;
}
async function getAndDisplayMessages() {
const messages = await messagingApi.getMessages();
for (const message of messages) {
const messageAlreadyDisplayed = message.id in displayedMessages;
if (!messageAlreadyDisplayed) displayMessage(message);
}
}
function pollMessages() { setInterval(getAndDisplayMessages, 3000); }
function streamMessages() {
const messagingSocket = messagingApi.createMessagingSocket();
messagingSocket.on('message', data => {
const message = JSON.parse(data);
const messageAlreadyDisplayed = message.id in displayedMessages;
if (!messageAlreadyDisplayed) displayMessage(message); });
}
if (process.env.MODE === 'poll') {
getAndDisplayMessages();
pollMessages(); }
else
if (process.env.MODE === 'stream')
{ getAndDisplayMessages();
streamMessages();
}

Rate Limiting

const database = { [index.html]: '<html>Hello World!</html>', }; 
module.exports.get = (key, callback) => {
setTimeout(() => {
callback(database[key]); }, 1000);
};
const database = require('./database'); 
const express = require('express');
const app = express();
app.listen(3000, () => console.log('Listening on port 3000.'));
// Keep a hash table of the previous access time for each user.
const accesses = {};
app.get('/index.html', function(req, res) {
const {user} = req.headers; i
f (user in accesses)
{ const previousAccessTime = accesses[user];
// Limit to 1 request every 5 seconds.
if (Date.now() - previousAccessTime < 5000) {
res.status(429).send('Too many requests.\n');
return;
}
}
// Serve the page and store this access time.
database.get('index.html', page => {
accesses[user] = Date.now(); res.send(page + '\n');
});
});

Logging and Monitoring

Pub/Sub Pattern

const axios = require('axios'); 
const WebSocket = require('ws');
function publish(message, topicId) { return axios.post('http://localhost:3001/${topicId}' , message); }
function subscribe(topicId) { return new WebSocket('ws://localhost:3001/$topicId }'); }
module.exports.publish = publish;
module.exports.subscribe = subscribe;
const express = require('express'); 
const expressWs = require('express—ws');
const app = express(); expressWs(app);
const sockets = {};
app.use(express.json());
app.listen(3001, () =>
{ console.log('Listening on port 3001!'); });
app.post('/:topicId', (req, res) => {
const {topicId} = req.params;
const message = req.body;
const topicSockets = sockets[topicId] || [];
for (const socket of topicSockets) {
socket.send(JSON.stringify(message)); }
});
app.ws('/:topicId', (socket, req) => {
const ItopicIdl = req.params;
if (!sockets[topicId]) sockets[topicId] = [];
const topicSockets = sockets[topicId];
topicSockets.push(socket);
socket.on('close', () => { topicSockets.splice(topicSockets.index0f(socket), 1);
});
});
const messagingApi = require('imessaging_api'); 
const readline = require('readline');
const TOPIC_ID = process.env.TOPIC_ID;
const terminal = readline.createInterface({ input: process.stdin, });
terminal.on('line', text => {
const name = process.env.NAME;
const message = {name, text}; messagingApi.publish(message, TOPIC_ID);
});
const messagingApi = require('imessaging_api'); 
const TOPIC_ID = process.env.TOPIC_ID;
function displayMessage(message) {
console.log('> ${message.name}: ${message.text}' }
function streamMessages() {
const messagingSocket = messagingApi.subscribe(TOPIC_ID);
messagingSocket.on('message'),(data) =>{
const message = JSON.parse(data); displayMessage(message); });
}
streamMessages();

--

--

Love writing, learning, sharing and love being sarcastic :)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store