├── README.md ├── amqpClient.js ├── app.js ├── package.json └── rpcServer.js /README.md: -------------------------------------------------------------------------------- 1 | # rabbitmq-with-expressjs 2 | 3 | By following [tutorials](https://www.rabbitmq.com/getstarted.html) from RabbitMQ official site. There is a problem for me to adapt the code from tutorials and use it in my project, since most examples not running on long-lived process. 4 | 5 | After googling for a pattern I could use, I ended up found this [great article](https://facundoolano.wordpress.com/2016/06/26/real-world-rpc-with-rabbitmq-and-node-js/) 6 | 7 | So here I will use pattern I found from the article along with express.js to create producer, 8 | and use this [tutorial](https://www.rabbitmq.com/tutorials/tutorial-six-javascript.html) to create consumer. 9 | 10 | ## Prerequisite 11 | * node & npm 12 | * rabbitmq 13 | 14 | ## Installation 15 | ``` 16 | npm install 17 | ``` 18 | 19 | ## Start RabbitMQ server 20 | ``` 21 | rabbitmq-server 22 | ``` 23 | 24 | ## Start application 25 | ``` 26 | # first terminal 27 | node app.js 28 | 29 | # second terminal 30 | node rpcServer.js 31 | ``` 32 | 33 | Then navigate to "localhost:3000/fibonacci/15" and you will see the result. 34 | -------------------------------------------------------------------------------- /amqpClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const amqp = require('amqplib'); 3 | const EventEmitter = require('events'); 4 | 5 | // this queue name will be attached to "replyTo" property on producer's message, 6 | // and the consumer will use it to know which queue to the response back to the producer 7 | const REPLY_QUEUE = 'amq.rabbitmq.reply-to'; 8 | 9 | /** 10 | * Create amqp channel and return back as a promise 11 | * @params {Object} setting 12 | * @params {String} setting.url 13 | * @returns {Promise} - return amqp channel 14 | */ 15 | const createClient = (setting) => amqp.connect(setting.url) 16 | .then(conn => conn.createChannel()) // create channel 17 | .then(channel => { 18 | channel.responseEmitter = new EventEmitter(); 19 | channel.responseEmitter.setMaxListeners(0); 20 | channel.consume(REPLY_QUEUE, 21 | msg => channel.responseEmitter.emit(msg.properties.correlationId, msg.content), 22 | { noAck: true }); 23 | return channel; 24 | }); 25 | 26 | /** 27 | * Send RPC message to waiting queue and return promise object when 28 | * event has been emitted from the "consume" function 29 | * @params {Object} channel - amqp channel 30 | * @params {String} message - message to send to consumer 31 | * @params {String} rpcQueue - name of the queue where message will be sent to 32 | * @returns {Promise} - return msg that send back from consumer 33 | */ 34 | const sendRPCMessage = (channel, message, rpcQueue) => new Promise(resolve => { 35 | // unique random string 36 | const correlationId = generateUuid(); 37 | 38 | channel.responseEmitter.once(correlationId, resolve); 39 | channel.sendToQueue(rpcQueue, new Buffer(message), { correlationId, replyTo: REPLY_QUEUE }); 40 | }); 41 | 42 | // this function will be used to generate random string to use as a correlation ID 43 | function generateUuid() { 44 | return Math.random().toString() + 45 | Math.random().toString() + 46 | Math.random().toString(); 47 | } 48 | 49 | module.exports.createClient = createClient; 50 | module.exports.sendRPCMessage = sendRPCMessage; -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | const amqpClient = require('./amqpClient'); 7 | 8 | let channel; 9 | amqpClient.createClient({ url: 'amqp:localhost' }) 10 | .then(ch => { 11 | // channel is kept for later use 12 | channel = ch; 13 | }); 14 | 15 | 16 | app.get('/fibonacci/:number', function(req, res) { 17 | const number = req.params.number; 18 | amqpClient.sendRPCMessage(channel, number, 'rpc_queue') 19 | .then(msg => { 20 | const result = JSON.parse(msg.toString()); 21 | res.json(result); 22 | }); 23 | }); 24 | 25 | const server = require('http').createServer(app); 26 | server.listen(3000, function() { 27 | console.log('App started.'); 28 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rabbitmq-with-expressjs", 3 | "version": "1.0.0", 4 | "description": "An example of how to use amqp protocol from rabbitmq to run on REST API on express.js", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "rabbitmq", 11 | "express.js", 12 | "amqp", 13 | "rest api", 14 | "rpc" 15 | ], 16 | "author": "Suratin Pattanawongthai (Murnax)", 17 | "email": "suratin.elec@gmail.com", 18 | "license": "ISC", 19 | "dependencies": { 20 | "amqplib": "^0.5.1", 21 | "express": "^4.15.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rpcServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const amqp = require('amqplib'); 4 | 5 | const q = 'rpc_queue'; 6 | amqp.connect('amqp://localhost') 7 | .then(conn => { 8 | return conn.createChannel(); 9 | }) 10 | .then(ch => { 11 | ch.assertQueue(q, { durable: false }); 12 | ch.prefetch(1); 13 | console.log(" [x] Awaiting RPC Requests"); 14 | ch.consume(q, msg => { 15 | 16 | const n = parseInt(msg.content.toString()); 17 | 18 | console.log(" [.] fib(%d)", n); 19 | 20 | // start 21 | let tStart = Date.now(); 22 | 23 | let r = fibonacci(n); 24 | 25 | // finish 26 | let tEnd = Date.now(); 27 | 28 | // to send object as a message, 29 | // you have to call JSON.stringify 30 | r = JSON.stringify({ 31 | result: r, 32 | time: (tEnd - tStart) 33 | }); 34 | 35 | ch.sendToQueue(msg.properties.replyTo, 36 | new Buffer(r.toString()), 37 | { correlationId: msg.properties.correlationId }); 38 | ch.ack(msg); 39 | }) 40 | }); 41 | 42 | function fibonacci(n) { 43 | if (!n) n = 1; 44 | 45 | if (n === 0 || n === 1) 46 | return n; 47 | else 48 | return fibonacci(n - 1) + fibonacci(n - 2); 49 | } --------------------------------------------------------------------------------