├── .gitignore ├── LICENSE ├── README.md ├── client.js ├── package-lock.json ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | 4 | # Logs 5 | logs 6 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Igor Likhomanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Example of RabbitMQ RPC Direct reply-to with node.js 2 | 3 | Details: [Docs on RabbitMQ](https://www.rabbitmq.com/direct-reply-to.html) 4 | 5 | ### How to start 6 | 7 | 1) RabbitMQ is required (e.g. with docker) 8 | 9 | ``` 10 | docker run -p 5672:5672 rabbitmq:3 11 | ``` 12 | 13 | 2) Install packages 14 | 15 | ``` 16 | npm i 17 | ``` 18 | 19 | 3) Start server 20 | 21 | ``` 22 | node server.js 23 | ``` 24 | 25 | 4) Execute client request 26 | 27 | ``` 28 | node client.js 29 | ``` 30 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | const amqp = require('amqplib'); 2 | const EventEmitter = require('events'); 3 | const uuid = require('uuid'); 4 | 5 | const RABBITMQ = 'amqp://guest:guest@localhost:5672'; 6 | 7 | // pseudo-queue for direct reply-to 8 | const REPLY_QUEUE = 'amq.rabbitmq.reply-to'; 9 | const q = 'example'; 10 | 11 | // Credits for Event Emitter goes to https://github.com/squaremo/amqp.node/issues/259 12 | 13 | const createClient = rabbitmqconn => 14 | amqp 15 | .connect(rabbitmqconn) 16 | .then(conn => conn.createChannel()) 17 | .then(channel => { 18 | channel.responseEmitter = new EventEmitter(); 19 | channel.responseEmitter.setMaxListeners(0); 20 | channel.consume( 21 | REPLY_QUEUE, 22 | msg => { 23 | channel.responseEmitter.emit( 24 | msg.properties.correlationId, 25 | msg.content.toString('utf8'), 26 | ); 27 | }, 28 | { noAck: true }, 29 | ); 30 | return channel; 31 | }); 32 | 33 | const sendRPCMessage = (channel, message, rpcQueue) => 34 | new Promise(resolve => { 35 | const correlationId = uuid.v4(); 36 | channel.responseEmitter.once(correlationId, resolve); 37 | channel.sendToQueue(rpcQueue, Buffer.from(message), { 38 | correlationId, 39 | replyTo: REPLY_QUEUE, 40 | }); 41 | }); 42 | 43 | const init = async () => { 44 | const channel = await createClient(RABBITMQ); 45 | const message = { uuid: uuid.v4() }; 46 | 47 | console.log(`[ ${new Date()} ] Message sent: ${JSON.stringify(message)}`); 48 | 49 | const respone = await sendRPCMessage(channel, JSON.stringify(message), q); 50 | 51 | console.log(`[ ${new Date()} ] Message received: ${respone}`); 52 | 53 | process.exit(); 54 | }; 55 | 56 | try { 57 | init(); 58 | } catch (e) { 59 | console.log(e); 60 | } 61 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-rabbit-rpc-direct-reply-to", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-rabbit-rpc-direct-reply-to", 9 | "version": "0.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "amqplib": "^0.10.0", 13 | "uuid": "^8.3.2" 14 | }, 15 | "devDependencies": {} 16 | }, 17 | "node_modules/amqplib": { 18 | "version": "0.10.0", 19 | "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.0.tgz", 20 | "integrity": "sha512-UueEnRGY6upiSvGsSYM22Woa1SeSukqYtqgYW4Gj8gHvbf5BRhhYRqf3kQ8aSUYYffTOZi6SeOVW2eOXt0hpPA==", 21 | "dependencies": { 22 | "bitsyntax": "~0.1.0", 23 | "buffer-more-ints": "~1.0.0", 24 | "readable-stream": "1.x >=1.1.9", 25 | "url-parse": "~1.5.10" 26 | }, 27 | "engines": { 28 | "node": ">=10" 29 | } 30 | }, 31 | "node_modules/bitsyntax": { 32 | "version": "0.1.0", 33 | "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", 34 | "integrity": "sha512-ikAdCnrloKmFOugAfxWws89/fPc+nw0OOG1IzIE72uSOg/A3cYptKCjSUhDTuj7fhsJtzkzlv7l3b8PzRHLN0Q==", 35 | "dependencies": { 36 | "buffer-more-ints": "~1.0.0", 37 | "debug": "~2.6.9", 38 | "safe-buffer": "~5.1.2" 39 | }, 40 | "engines": { 41 | "node": ">=0.8" 42 | } 43 | }, 44 | "node_modules/buffer-more-ints": { 45 | "version": "1.0.0", 46 | "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", 47 | "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" 48 | }, 49 | "node_modules/core-util-is": { 50 | "version": "1.0.2", 51 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 52 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 53 | }, 54 | "node_modules/debug": { 55 | "version": "2.6.9", 56 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 57 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 58 | "dependencies": { 59 | "ms": "2.0.0" 60 | } 61 | }, 62 | "node_modules/inherits": { 63 | "version": "2.0.3", 64 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 65 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 66 | }, 67 | "node_modules/isarray": { 68 | "version": "0.0.1", 69 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 70 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 71 | }, 72 | "node_modules/ms": { 73 | "version": "2.0.0", 74 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 75 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 76 | }, 77 | "node_modules/querystringify": { 78 | "version": "2.2.0", 79 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 80 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 81 | }, 82 | "node_modules/readable-stream": { 83 | "version": "1.1.14", 84 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 85 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 86 | "dependencies": { 87 | "core-util-is": "~1.0.0", 88 | "inherits": "~2.0.1", 89 | "isarray": "0.0.1", 90 | "string_decoder": "~0.10.x" 91 | } 92 | }, 93 | "node_modules/requires-port": { 94 | "version": "1.0.0", 95 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 96 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" 97 | }, 98 | "node_modules/safe-buffer": { 99 | "version": "5.1.2", 100 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 101 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 102 | }, 103 | "node_modules/string_decoder": { 104 | "version": "0.10.31", 105 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 106 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 107 | }, 108 | "node_modules/url-parse": { 109 | "version": "1.5.10", 110 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", 111 | "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", 112 | "dependencies": { 113 | "querystringify": "^2.1.1", 114 | "requires-port": "^1.0.0" 115 | } 116 | }, 117 | "node_modules/uuid": { 118 | "version": "8.3.2", 119 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 120 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 121 | "bin": { 122 | "uuid": "dist/bin/uuid" 123 | } 124 | } 125 | }, 126 | "dependencies": { 127 | "amqplib": { 128 | "version": "0.10.0", 129 | "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.0.tgz", 130 | "integrity": "sha512-UueEnRGY6upiSvGsSYM22Woa1SeSukqYtqgYW4Gj8gHvbf5BRhhYRqf3kQ8aSUYYffTOZi6SeOVW2eOXt0hpPA==", 131 | "requires": { 132 | "bitsyntax": "~0.1.0", 133 | "buffer-more-ints": "~1.0.0", 134 | "readable-stream": "1.x >=1.1.9", 135 | "url-parse": "~1.5.10" 136 | } 137 | }, 138 | "bitsyntax": { 139 | "version": "0.1.0", 140 | "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", 141 | "integrity": "sha512-ikAdCnrloKmFOugAfxWws89/fPc+nw0OOG1IzIE72uSOg/A3cYptKCjSUhDTuj7fhsJtzkzlv7l3b8PzRHLN0Q==", 142 | "requires": { 143 | "buffer-more-ints": "~1.0.0", 144 | "debug": "~2.6.9", 145 | "safe-buffer": "~5.1.2" 146 | } 147 | }, 148 | "buffer-more-ints": { 149 | "version": "1.0.0", 150 | "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", 151 | "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" 152 | }, 153 | "core-util-is": { 154 | "version": "1.0.2", 155 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 156 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 157 | }, 158 | "debug": { 159 | "version": "2.6.9", 160 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 161 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 162 | "requires": { 163 | "ms": "2.0.0" 164 | } 165 | }, 166 | "inherits": { 167 | "version": "2.0.3", 168 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 169 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 170 | }, 171 | "isarray": { 172 | "version": "0.0.1", 173 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 174 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 175 | }, 176 | "ms": { 177 | "version": "2.0.0", 178 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 179 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 180 | }, 181 | "querystringify": { 182 | "version": "2.2.0", 183 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 184 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 185 | }, 186 | "readable-stream": { 187 | "version": "1.1.14", 188 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 189 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 190 | "requires": { 191 | "core-util-is": "~1.0.0", 192 | "inherits": "~2.0.1", 193 | "isarray": "0.0.1", 194 | "string_decoder": "~0.10.x" 195 | } 196 | }, 197 | "requires-port": { 198 | "version": "1.0.0", 199 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 200 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" 201 | }, 202 | "safe-buffer": { 203 | "version": "5.1.2", 204 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 205 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 206 | }, 207 | "string_decoder": { 208 | "version": "0.10.31", 209 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 210 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 211 | }, 212 | "url-parse": { 213 | "version": "1.5.10", 214 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", 215 | "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", 216 | "requires": { 217 | "querystringify": "^2.1.1", 218 | "requires-port": "^1.0.0" 219 | } 220 | }, 221 | "uuid": { 222 | "version": "8.3.2", 223 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 224 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-rabbit-rpc-direct-reply-to", 3 | "version": "0.0.1", 4 | "description": "Example of RabbitMQ RPC Direct reply-to with node.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [ 11 | "rpc", 12 | "node.js", 13 | "rabbitmq" 14 | ], 15 | "author": "Igor Likhomanov", 16 | "license": "MIT", 17 | "dependencies": { 18 | "amqplib": "^0.10.0", 19 | "uuid": "^8.3.2" 20 | }, 21 | "directories": { 22 | "lib": "lib" 23 | }, 24 | "devDependencies": {} 25 | } 26 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const { v4: uuidv4 } = require('uuid'); 2 | 3 | const RABBITMQ = 'amqp://guest:guest@localhost:5672'; 4 | 5 | const open = require('amqplib').connect(RABBITMQ); 6 | const q = 'example'; 7 | 8 | // Consumer 9 | open 10 | .then(function (conn) { 11 | console.log(`[ ${new Date()} ] Server started`); 12 | return conn.createChannel(); 13 | }) 14 | .then(function (ch) { 15 | return ch.assertQueue(q).then(function (ok) { 16 | return ch.consume(q, function (msg) { 17 | console.log( 18 | `[ ${new Date()} ] Message received: ${JSON.stringify( 19 | JSON.parse(msg.content.toString('utf8')), 20 | )}`, 21 | ); 22 | if (msg !== null) { 23 | const response = { 24 | uuid: uuidv4(), 25 | }; 26 | 27 | console.log( 28 | `[ ${new Date()} ] Message sent: ${JSON.stringify(response)}`, 29 | ); 30 | 31 | ch.sendToQueue( 32 | msg.properties.replyTo, 33 | Buffer.from(JSON.stringify(response)), 34 | { 35 | correlationId: msg.properties.correlationId, 36 | }, 37 | ); 38 | 39 | ch.ack(msg); 40 | } 41 | }); 42 | }); 43 | }) 44 | .catch(console.warn); 45 | --------------------------------------------------------------------------------