<style> .textleft { text-align:left; } .reveal, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { font-family:Arial, Microsoft JhengHei;} .small-font { font-size: 20px !important; } .reveal .progress { height: 20px !important; transform: scale(1,2); } .progress span { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAMCAIAAAAs6UAAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUNCQzIyREQ0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUNCQzIyREU0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQ0JDMjJEQjRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0JDMjJEQzRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PovDFgYAAAAmSURBVHjaYvjPwMAAxjMZmBhA9H8INv4P4TPM/A+m04zBNECAAQBCWQv9SUQpVgAAAABJRU5ErkJggg==) repeat-x !important; } .progress span:after, .progress span.nyancat { content: ""; background: url(data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8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) ; width: 36px !important; height: 21px !important; border: none !important; float: right; margin-top: -7px; margin-right: -10px; transform: scale(2,1); } .fullimg { height: 550px !important; } .reveal section img { background: white; } </style> <!-- .slide: data-transition="slide" --> # RabbitMQ @Kais(VagrantPi) ###### tags: `slide`, `簡報`, `RabbitMQ` --- <!-- .slide: data-transition="slide" --> ## Agenda - Hello World! - Work queues - Publish/Subscribe - Routing - Topics - RPC - sample code link --- <!-- .slide: data-transition="slide" --> ### Hello World! ![](https://www.rabbitmq.com/img/tutorials/python-one.png) ---- <!-- .slide: data-transition="convex" --> send.js(producer) ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var queue = 'hello'; var msg = 'Hello World!'; // 先戳一下 queue,如果不存在就建立 // 也可以在建立 queue 時帶入參數 // durable: if true, the queue will survive broker restarts channel.assertQueue(queue, { durable: false }); channel.sendToQueue(queue, Buffer.from(msg)); console.log(" [x] Sent %s", msg); }); setTimeout(function() { connection.close(); process.exit(0); }, 500); }); ``` ---- <!-- .slide: data-transition="convex" --> receive.js(consumers) ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var queue = 'hello'; // 需要注意的是,所有 assertQueue 'hello' queue 帶的參數要一樣,不然會噴 error channel.assertQueue(queue, { durable: false }); console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue); channel.consume(queue, function(msg) { console.log(" [x] Received %s", msg.content.toString()); }, { noAck: true }); }); }); ``` --- <!-- .slide: data-transition="slide" --> ### Work queues ![](https://www.rabbitmq.com/img/tutorials/python-two.png) ``` 預設值情況下,RabbitMQ 會輪著分配,所以在偶數 tack 情況下,每個 worker 分配到的工作是一樣的,這稱為:round-robin ``` ---- <!-- .slide: data-transition="convex" --> #### acknowledgment ```javascript noAck: true channel.consume(queue, function(msg) { .... }, {noAck: false});// 開啟 acknowledgment ``` ``` 當 worker 拿到 task 掛掉後,RabbitMQ 可以透過 acknowledgment 來發 現,並將 task 在分派下去,原理及 worker 接收到後向 RabbitMQ 發送訊息 所已經取得了 `channel.ack(msg);` ``` ---- <!-- .slide: data-transition="convex" --> #### Message durability ``` 上面的 acknowledgment 使 consumer 掛掉後,任務不會遺失,但假設 RabbitMQ 主機掛掉,task 還是會遺失,因此需要設定 queue 為 durable ,然後傳送的 message 必須設定 persistent ``` ```javascript channel.assertQueue('hello',{ durable:true }); ``` ``` 但是,assertQueue 原本以建立的 Queue 餵的參數不能跟當初建立時的 不同 ``` ```javascript channel.sendToQueue(q, Buffer.from(msg), {persistent: true}); ``` ---- <!-- .slide: data-transition="convex" --> #### Fair dispatch ![](https://www.rabbitmq.com/img/tutorials/prefetch-count.png) ```javascript channel.prefetch(1); ``` ``` 前面有提到預設採用 round-robin 的分配 task 方式,所以不管 worker 是 否還在執行 task,他還是會往他那邊塞,prefetch=1 的意思為 worker 同 一時間只會接收一個 task ``` ---- <!-- .slide: data-transition="convex" --> new_task.js(producer) ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var queue = 'task_queue'; var msg = process.argv.slice(2).join(' ') || "Hello World!"; channel.assertQueue(queue, { durable: true }); channel.sendToQueue(queue, Buffer.from(msg), { persistent: true }); console.log(" [x] Sent '%s'", msg); }); setTimeout(function() { connection.close(); process.exit(0); }, 500); }); ``` ---- <!-- .slide: data-transition="convex" --> worker.js(consumers) ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error, connection) { connection.createChannel(function(error, channel) { var queue = 'task_queue'; channel.assertQueue(queue, { durable: true }); channel.prefetch(1); console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue); channel.consume(queue, function(msg) { var secs = msg.content.toString().split('.').length - 1; console.log(" [x] Received %s", msg.content.toString()); setTimeout(function() { console.log(" [x] Done"); channel.ack(msg); }, secs * 1000); }, { noAck: false }); }); }); ``` --- <!-- .slide: data-transition="slide" --> ### Publish/Subscribe ![](https://www.rabbitmq.com/img/tutorials/exchanges.png) ``` 要實作一個 log 記錄器,在寫入硬碟的同時(worker1),並輸出至螢幕(worker2) ``` ---- <!-- .slide: data-transition="convex" --> #### Exchanges ``` 這邊在架構上引入一個新角色 - Exchanges,producer 透過 Exchanges 分排到你預期的 queue 中,可以使其更加彈性,缺點則是當單一 Exchanges 故障,正個 service 就會停擺,Exchanges 提供 direct, topic, headers, fanout 不同模式,該範例以 fanout 為主 ``` ---- <!-- .slide: data-transition="convex" --> #### Temporary queues 對於一些需求上,可能不需要明確的建立 queue(只需要暫存的) ```javascript channel.assertQueue('',{ // exclusive 為 true 時,只會讓一個 connection 連接,並在結束時會刪除該 queue exclusive:true }); ``` ---- <!-- .slide: data-transition="convex" --> #### Bindings ![](https://www.rabbitmq.com/img/tutorials/bindings.png) ``` 建立完 Exchange 和 Queue 後,需要綁定兩者之間的關係 ``` ---- <!-- .slide: data-transition="convex" --> #### Putting it all together ![](https://www.rabbitmq.com/img/tutorials/python-three-overall.png) ---- <!-- .slide: data-transition="convex" --> emit_log.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'logs'; var msg = process.argv.slice(2).join(' ') || 'Hello World!'; channel.assertExchange(exchange, 'fanout', { durable: false }); channel.publish(exchange, '', Buffer.from(msg)); console.log(" [x] Sent %s", msg); }); setTimeout(function() { connection.close(); process.exit(0); }, 500); }); ``` ---- <!-- .slide: data-transition="convex" --> receive_logs.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'logs'; channel.assertExchange(exchange, 'fanout', { durable: false }); channel.assertQueue('', { exclusive: true }, function(error2, q) { if (error2) { throw error2; } console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", q.queue); // channel.bindQueue(queue, source, pattern, [args, [function(err, ok) {...}]]) channel.bindQueue(q.queue, exchange, ''); channel.consume(q.queue, function(msg) { if (msg.content) { console.log(" [x] %s", msg.content.toString()); } }, { noAck: true }); }); }); }); ``` ---- <!-- .slide: data-transition="convex" --> ``` ./receive_logs.js > logs_from_rabbit.log ``` ``` ./receive_logs.js ``` ``` ./emit_log.js ``` --- <!-- .slide: data-transition="slide" --> ### Routing ![](https://www.rabbitmq.com/img/tutorials/python-four.png) ---- <!-- .slide: data-transition="convex" --> #### Bindings ```javascript channel.bindQueue(q.queue, exchange, ''); ``` bindQueue 可以簡單理解成該 queue 只對什麼 exchanges 訊息有興趣 ---- <!-- .slide: data-transition="convex" --> #### Direct exchange ![](https://www.rabbitmq.com/img/tutorials/direct-exchange.png) message(routing key) -> direct + binding key ---- <!-- .slide: data-transition="convex" --> #### Multiple bindings ![](https://www.rabbitmq.com/img/tutorials/direct-exchange-multiple.png) ---- <!-- .slide: data-transition="convex" --> emit_log_direct.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'direct_logs'; var args = process.argv.slice(2); var msg = args.slice(1).join(' ') || 'Hello World!'; var severity = (args.length > 0) ? args[0] : 'info'; channel.assertExchange(exchange, 'direct', { durable: false }); channel.publish(exchange, severity, Buffer.from(msg)); console.log(" [x] Sent %s: '%s'", severity, msg); }); setTimeout(function() { connection.close(); process.exit(0) }, 500); }); ``` ---- <!-- .slide: data-transition="convex" --> receive_logs_direct.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); var args = process.argv.slice(2); if (args.length == 0) { console.log("Usage: receive_logs_direct.js [info] [warning] [error]"); process.exit(1); } amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'direct_logs'; channel.assertExchange(exchange, 'direct', { durable: false }); channel.assertQueue('', { exclusive: true }, function(error2, q) { if (error2) { throw error2; } console.log(' [*] Waiting for logs. To exit press CTRL+C'); args.forEach(function(severity) { channel.bindQueue(q.queue, exchange, severity); }); channel.consume(q.queue, function(msg) { console.log(" [x] %s: '%s'", msg.fields.routingKey, msg.content.toString()); }, { noAck: true }); }); }); }); ``` ---- <!-- .slide: data-transition="convex" --> ![](https://www.rabbitmq.com/img/tutorials/python-four.png) ``` ./receive_logs_direct.js warning error > logs_from_rabbit.log ``` ``` ./receive_logs_direct.js info warning error ``` ``` ./emit_log_direct.js error "Run. Run. Or it will explode." ``` --- <!-- .slide: data-transition="slide" --> ### Topics ![](https://www.rabbitmq.com/img/tutorials/python-five.png) ---- <!-- .slide: data-transition="convex" --> 又回到我們的 log 服務上,log 不只有各種 severity level 的差別 (info/warn/crit...) 也會有其他像 facility (auth/cron/kern...). ---- <!-- .slide: data-transition="convex" --> #### Topic exchange ``` 設定廣播的 topic 都用 `.` 隔開,ex: "stock.usd.nyse", "nyse.vmw" , "quick.orange.rabbit(最多可到 255 bytes) binding 的方式也跟 direct 很像,選定要訂閱的 topic 就好 - `*` 可代替一個單字 - `#` 可代替 0 或多個單字 ex: *.orange.*", "*.*.rabbit", "lazy.#" ``` ---- <!-- .slide: data-transition="convex" --> 範例 ``` Queue1: "*.orange.*" Queue2: "*.*.rabbit", "lazy.#" routing key: "quick.orange.rabbit" ※ 如果送出的 lazy.orange.male.rabbit 這種長度跟 "*.orange.*", "*.*.rabbit" 的合,不過跟 "lazy.#" 有 match,所以只會傳到 Queue2 ``` ---- <!-- .slide: data-transition="convex" --> emit_log_topic.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'topic_logs'; var args = process.argv.slice(2); var key = (args.length > 0) ? args[0] : 'anonymous.info'; var msg = args.slice(1).join(' ') || 'Hello World!'; channel.assertExchange(exchange, 'topic', { durable: false }); channel.publish(exchange, key, Buffer.from(msg)); console.log(" [x] Sent %s:'%s'", key, msg); }); setTimeout(function() { connection.close(); process.exit(0) }, 500); }); ``` ---- <!-- .slide: data-transition="convex" --> receive_logs_topic.js: ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); var args = process.argv.slice(2); if (args.length == 0) { console.log("Usage: receive_logs_topic.js <facility>.<severity>"); process.exit(1); } amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var exchange = 'topic_logs'; channel.assertExchange(exchange, 'topic', { durable: false }); channel.assertQueue('', { exclusive: true }, function(error2, q) { if (error2) { throw error2; } console.log(' [*] Waiting for logs. To exit press CTRL+C'); args.forEach(function(key) { channel.bindQueue(q.queue, exchange, key); }); channel.consume(q.queue, function(msg) { console.log(" [x] %s:'%s'", msg.fields.routingKey, msg.content.toString()); }, { noAck: true }); }); }); }); ``` --- <!-- .slide: data-transition="slide" --> ### Remote procedure call (RPC) ![](https://www.rabbitmq.com/img/tutorials/python-six.png) ---- <!-- .slide: data-transition="convex" --> #### Callback queue ```javascript channel.assertQueue('', { exclusive: true }); channel.sendToQueue('rpc_queue', Buffer.from('10'), { replyTo: queue_name }); # ... then code to read a response message from the callback queue ... ``` ---- <!-- .slide: data-transition="convex" --> #### Message properties ``` AMQP 0-9-1 protocol predefines a set of 14 properties * persistent: tutorial 2 有提到,可以確保 restart 後資料還在 * content_type: mime-type,ex: `application/json`. * reply_to: callback queue 的名字 * correlation_id: 識別 Useful to correlate RPC responses with requests. ``` ---- <!-- .slide: data-transition="convex" --> #### Correlation Id 如果每次的 RPC callback 都建立一個 callback queue 這樣沒什麼效率,所以好的方法是只建立一個 callback 的 queue,不過為了分辨這次 callback 是哪一次哪一個 producer 傳的,所以需要一個 Correlation Id 來做識別 ---- <!-- .slide: data-transition="convex" --> rpc_client.js ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); var args = process.argv.slice(2); if (args.length === 0) { console.log("Usage: rpc_client.js num"); process.exit(1); } amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } channel.assertQueue('', { exclusive: true }, function(error2, q) { if (error2) { throw error2; } var correlationId = generateUuid(); var num = parseInt(args[0]); console.log(' [x] Requesting fib(%d)', num); channel.consume(q.queue, function(msg) { if (msg.properties.correlationId === correlationId) { console.log(' [.] Got %s', msg.content.toString()); setTimeout(function() { connection.close(); process.exit(0); }, 500); } }, { noAck: true }); channel.sendToQueue('rpc_queue', Buffer.from(num.toString()), { correlationId: correlationId, replyTo: q.queue }); }); }); }); function generateUuid() { return Math.random().toString() + Math.random().toString() + Math.random().toString(); } ``` ---- <!-- .slide: data-transition="convex" --> ```javascript #!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(error0, connection) { if (error0) { throw error0; } connection.createChannel(function(error1, channel) { if (error1) { throw error1; } var queue = 'rpc_queue'; channel.assertQueue(queue, { durable: false }); channel.prefetch(1); console.log(' [x] Awaiting RPC requests'); channel.consume(queue, function reply(msg) { var n = parseInt(msg.content.toString()); console.log(" [.] fib(%d)", n); var r = fibonacci(n); channel.sendToQueue(msg.properties.replyTo, Buffer.from(r.toString()), { correlationId: msg.properties.correlationId }); channel.ack(msg); }); }); }); function fibonacci(n) { if (n === 0 || n === 1) return n; else return fibonacci(n - 1) + fibonacci(n - 2); } ``` --- <!-- .slide: data-transition="slide" --> ## sample code - [sample code](https://bitbucket.org/kaislin/bolt-queue/src/master/) - [官方 tutorials code](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/javascript-nodejs) - [node.js amqplib sample code](https://github.com/squaremo/amqp.node/tree/master/examples/tutorials)
{"metaMigratedAt":"2023-06-14T21:42:57.409Z","metaMigratedFrom":"Content","title":"RabbitMQ","breaks":true,"contributors":"[{\"id\":\"69ade472-3ed3-499d-8a69-767243a31621\",\"add\":28103,\"del\":3888}]"}
    482 views