├── .gitignore ├── README.md ├── imgs ├── mq_pub_sub.png ├── mq_pubsub.png ├── mq_routeing.png ├── mq_routing.png ├── mq_rpc1.png ├── mq_simplest.png ├── mq_topic.png ├── mq_topic1.png └── mq_work_queue.png ├── index.js ├── package.json └── src ├── messagepersistence.js ├── pubsub.js ├── routeing.js ├── rpc.js ├── simplest.js ├── topic.js └── workqueue.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.vscode 3 | /yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ 2 | 3 | ## 场景 4 | - 应用解耦(异步) 5 | - 通知 6 | - 限流 7 | - 数据分发 8 | 9 | 10 | 11 | ## 概念 12 | - 生产者 :生产消息 13 | - 消费者 :接收消息的 14 | - 消息队列 : 到达消费者前一刻存储的地方,因为消息也可以发给`exchange`,`exchange`肯定也有存储消息的地方 15 | - 通道:建立在连接之上 16 | - `Ack`回执:收到消息后确认消息已经消费的应答 17 | - `exchange` 路由交换 18 | - `routeing key` 路由键 19 | - `topic` 主题 20 | 21 | 22 | # simplest 简单队列 23 | 24 | ## 简单队列模型 25 | !['mq简单队列'](./imgs/mq_simplest.png) 26 | 27 | 一个生产者对应一个消费者 28 | 29 | 三个角色 30 | 31 | - 消息生产者 `Producer` 32 | - 消息中间件(提供消息队列) `Queue` 33 | - 消费消费者 `Consumer` 34 | 35 | 36 | # Work Queues 工作队列/任务队列 37 | 38 | ## 工作队列模型 39 | 40 | !['任务队列'](./imgs/mq_work_queue.png) 41 | 42 | 工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反,我们安排任务在以后完成。我们将任务封装 为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当您运行许多工作人员时,任务将在他们之间共享。 43 | 44 | - 工作队列 方式派发消息的方式的两种方式 45 | 46 | - 轮询 47 | - 任务队列 48 | 1. 关闭自动回执(ack) 49 | 2. 设置每次接受消息数 50 | 3. 手动回执 51 | 52 | 53 | 54 | # Publish/Subscribe 发布订阅 55 | 56 | ## 发布订阅模型 57 | 58 | !['发布订阅'](./imgs/mq_pubsub.png) 59 | 60 | 1. 一个生产者,多个消费者 61 | 2. 每一个消费者都有自己的队列 62 | 3. 生产者没有直接把消息发送到队列 而是发到了**交换机 转发器 exchange** 63 | 4. **每一个队列都要绑定到交换机上** 64 | 5. **生产者发送的消息 经过交换机 到达队列 就能实现一个消息被多个消费者消费** 65 | 66 | 67 | 大白话: 68 | - 一个富人说今天给`老人`(交换类型)发`钱`(消息) 69 | - 每一个 老人 带上自己的`碗`(队列)来装钱 70 | 71 | ## x 交换机 72 | 交换机把消息推到队列里面 73 | 74 | ### 交换类型 75 | - direct 76 | - topic 77 | - headers 78 | - fanout 79 | 80 | !['pubsub'](./imgs/mq_pub_sub.png) 81 | 82 | 83 | # Routing 84 | 85 | ## routeing 模型 86 | 87 | !['routeing'](./imgs/mq_routeing.png) 88 | 89 | 在`pub/sub`的基础上增加了 `routeing key`,可以选择性的接收消息 90 | 91 | !['routeing'](./imgs/mq_routeing.png) 92 | 93 | 94 | # Topics 95 | ## Topic模型 96 | !['topic'](./imgs/mq_topic.png) 97 | 98 | ## 特殊`routeing key`字符 99 | - `#` (哈希)可以替代零个或多个单词。 100 | - `*` 可以代替一个单词。 101 | 102 | **`exchange` 类型必须是`topic`** 103 | 104 | !['topic'](./imgs/mq_topic.png) 105 | 106 | 107 | 108 | # RPC 109 | 110 | ## RPC远程过程调用模型 111 | !['rpc'](./imgs/mq_rpc1.png) 112 | 113 | RPC即是远程调用服务端的方法,使用`MQ`可以实现`RPC`的异步调用,基于`Direct`交换机实现 114 | 1. 客户端即是生产者又是消费者,向`RPC`请求队列发送`RPC`调用消息,同时监听`RPC`响应队列 115 | 2. 服务端监听`RPC`请求队列,收到消息后执行服务端的方法 116 | 3. 服务端将方法执行后的结果发送到`RPC`响应队列 117 | 118 | ## 消息属性 119 | AMQP 0-9-1协议预定义了消息附带的14个属性集。除以下内容外,大多数属性很少使用: 120 | - `persistent` 121 | - `content_type` 122 | - `reply_to` 123 | - `correlation_id` 124 | 125 | # 消息应答和持久化 126 | 127 | ## 消息应答 128 | 129 | - noAck 130 | - `noAck = true` 自动确认模式 一旦 mq 将消息把消息分配给消费者,就会从内存中删除,如果业务方拿到消息并没有处理完,消息会丢失。 131 | - `noAck = false` 如果消费者挂了,没有发送回执,那么这条消息就没有被消费,会被其他消费者接收。**消费完成必须发送回执,告诉 MQ 已经消费完成**,不然会被其他消费者消费到 132 | 133 | ## 持久化 134 | 135 | - **`MQ`是存在内存中的,如果它挂了,我们的消息依然会丢失!!** 但是!!`RabbitMQ`可以支持持久化 136 | 137 | ```js 138 | channel.assertQueue(queue, { 139 | // 已经存在的队列不能修改 140 | // 非持久化 141 | durable: false 142 | }) 143 | ``` 144 | -------------------------------------------------------------------------------- /imgs/mq_pub_sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_pub_sub.png -------------------------------------------------------------------------------- /imgs/mq_pubsub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_pubsub.png -------------------------------------------------------------------------------- /imgs/mq_routeing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_routeing.png -------------------------------------------------------------------------------- /imgs/mq_routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_routing.png -------------------------------------------------------------------------------- /imgs/mq_rpc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_rpc1.png -------------------------------------------------------------------------------- /imgs/mq_simplest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_simplest.png -------------------------------------------------------------------------------- /imgs/mq_topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_topic.png -------------------------------------------------------------------------------- /imgs/mq_topic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_topic1.png -------------------------------------------------------------------------------- /imgs/mq_work_queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolliyong/node_rabbitMQ_mqtutorial/27bbafb6f383c4bda58eb739db44edf1ebb3febd/imgs/mq_work_queue.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const simplest = require('./src/simplest.js') 2 | const workqueue = require('./src/workqueue.js') 3 | const pubsub = require('./src/pubsub.js') 4 | const routeing = require('./src/routeing.js') 5 | const topic = require('./src/topic.js') 6 | const rpc = require('./src/rpc.js') 7 | 8 | // ------------ 简单队列 ------------- 9 | 10 | // 发送消息 11 | // simplest.send() 12 | 13 | // 发送消息 14 | // simplest.receive() 15 | 16 | // ------------ 工作队列 ------------- 17 | 18 | // 发送消息 19 | // workqueue.send() 20 | 21 | // //接收消息 22 | // workqueue.receive(1, 100) 23 | // // // 接受消息 24 | // workqueue.receive(2, 5000) 25 | 26 | 27 | 28 | 29 | // 发布订阅模式 30 | 31 | // 消费者监听消息中 32 | // pubsub.receive() 33 | 34 | // 生产者生产消息 35 | // pubsub.send() 36 | 37 | 38 | 39 | // routeing 模式 40 | // routeing.receive() 41 | 42 | 43 | // routeing.send() 44 | 45 | 46 | // 主题模式 47 | 48 | 49 | // setTimeout(()=>{ 50 | // topic.send() 51 | // },5000) 52 | 53 | // topic.receive() 54 | 55 | // RPC 调用 56 | rpc.client() 57 | rpc.service() -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_rabbit_mq", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "amqplib": "^0.5.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/messagepersistence.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-16 20:59:54 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 01:15:36 6 | */ 7 | 8 | // 简单队列 9 | const amqp = require('amqplib/callback_api') 10 | 11 | // 发送消息 12 | module.exports.send = async function() { 13 | /** 14 | * 1.连接mq 15 | * 2.创建通道 16 | * 3.声明队列 17 | * 4.创建消息 18 | * 5.发送消息 19 | */ 20 | // 1. 21 | amqp.connect('amqp://localhost:5672', function(error0, connection) { 22 | if (error0) { 23 | throw error0 24 | } 25 | // 2 26 | connection.createChannel(async function(error1, channel) { 27 | if (error1) { 28 | throw error1 29 | } 30 | // 3. 31 | const queue = 'simplest' 32 | // // 4. 33 | // const msg = 'Hello world' 34 | 35 | channel.assertQueue(queue, { 36 | durable: false 37 | }) 38 | // 5. 39 | for (let i = 0; i <= 10; i++) { 40 | await new Promise(resolve => { 41 | setTimeout(() => { 42 | channel.sendToQueue(queue, Buffer.from(`生产消息:${i}`)) 43 | console.log(`[p]<-- 生产消息:${i}`) 44 | resolve() 45 | }, 1500) 46 | }) 47 | } 48 | }) 49 | }) 50 | } 51 | 52 | // 接收消息 53 | module.exports.receive = async function() { 54 | /** 55 | * 1.连接mq 56 | * 2.创建通道 57 | * 3.声明队列 58 | * 4.创建回调函数,等待消息 59 | */ 60 | // 1. 61 | amqp.connect('amqp://localhost:5672', function(error0, connection) { 62 | if (error0) { 63 | throw error0 64 | } 65 | // 2 66 | connection.createChannel(function(error1, channel) { 67 | if (error1) { 68 | throw error1 69 | } 70 | var queue = 'simplest' 71 | 72 | channel.assertQueue(queue, { 73 | durable: false 74 | }) 75 | 76 | // 自动回执消息 77 | const opt = { 78 | noAck: true 79 | } 80 | // 每次消费一个消息 81 | channel.prefetch(1) 82 | // 消费消息 83 | channel.consume( 84 | queue, 85 | msg => { 86 | const msgText = msg.content.toString() 87 | console.log(`[c]--> 接收到消息:${msgText}`) 88 | }, 89 | opt 90 | ) 91 | }) 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /src/pubsub.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-16 23:41:36 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-17 22:31:49 6 | */ 7 | 8 | const amqp = require('amqplib/callback_api') 9 | // 发布订阅 10 | module.exports.send = function() { 11 | amqp.connect('amqp://localhost:5672', (err0, connection) => { 12 | if (err0) { 13 | throw err 14 | } 15 | // 创建通道 16 | connection.createChannel((err1, ch) => { 17 | if (err1) { 18 | throw err1 19 | } 20 | 21 | // 声明交换机 22 | const exchange_name = 'pb1' 23 | 24 | 25 | // 创建临时交换机 关闭持久化 26 | ch.assertExchange(exchange_name, 'fanout', { durable: false }) 27 | // 发布到交换机上 28 | // 参数1 交换机名字 参数2 指定队列 3 内容 29 | let msgCount = 1 30 | const timer = setInterval(() => { 31 | if(msgCount<=10){ 32 | ch.publish(exchange_name, '', Buffer.from(`发布订阅模式消息${msgCount}`)) 33 | msgCount++ 34 | }else{ 35 | clearInterval(timer) 36 | } 37 | }, 1500) 38 | console.log(`发送 消息到交换机 ${exchange_name}`) 39 | }) 40 | }) 41 | } 42 | 43 | module.exports.receive = function() { 44 | /** 45 | * 1. 。。。创建通道 46 | * 2. 声明交换机 47 | */ 48 | amqp.connect('amqp://localhost:5672', (err0, connect) => { 49 | if (err0) { 50 | throw err0 51 | } 52 | connect.createChannel((err1, ch) => { 53 | if (err1) { 54 | throw err1 55 | } 56 | // 声明交换机 57 | const exchange_name = 'pb1' 58 | // 通道和交换机 交互 59 | ch.assertExchange(exchange_name, 'fanout', { durable: false }) 60 | 61 | // 通道和队列 参数1 队列 参数2 交换类型, 62 | ch.assertQueue('', 'fanout', (err2, q) => { 63 | if (err2) { 64 | throw err2 65 | } 66 | 67 | // 绑定队列,参数1队列,参数2 路由 参数3 routeing key 68 | ch.bindQueue(q.queue, exchange_name, '') 69 | console.log(`绑定成功调用send方法`) 70 | // module.exports.send() 71 | let i = 1; 72 | ch.consume(q.queue, msg => { 73 | i++ 74 | console.log(`通过交换器找到的队列取出消息:${msg.content.toString()}`) 75 | if(i >= 10){ 76 | ch.close() 77 | } 78 | },{ 79 | noAck: true 80 | }) 81 | }) 82 | }) 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /src/routeing.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-17 22:31:47 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 00:20:13 6 | */ 7 | const amqp = require('amqplib/callback_api') 8 | 9 | module.exports.send = () => { 10 | amqp.connect('amqp://localhost:5672', (err0, connection) => { 11 | if (err0) { 12 | throw err 13 | } 14 | // 创建通道 15 | connection.createChannel((err1, ch) => { 16 | if (err1) { 17 | throw err1 18 | } 19 | 20 | // 还是声明交换 21 | const exchange = 'color' 22 | // 声明交换, direct , 不持续 23 | ch.assertExchange(exchange, 'direct', { durable: false }) 24 | 25 | setInterval(()=>{ 26 | 27 | ch.publish(exchange, 'red', Buffer.from(`red msg`)) 28 | console.log(`[red] msg 发送完毕`) 29 | 30 | ch.publish(exchange, 'blue', Buffer.from(`blue msg`)) 31 | console.log(`[blue] msg 发送完毕`) 32 | 33 | ch.publish(exchange, 'green', Buffer.from(`green msg`)) 34 | console.log(`[green] msg 发送完毕`) 35 | },1000) 36 | }) 37 | }) 38 | } 39 | 40 | module.exports.receive = () => { 41 | amqp.connect('amqp://localhost:5672', (err0, connection) => { 42 | if (err0) { 43 | throw err 44 | } 45 | // 创建通道 46 | connection.createChannel((err1, ch) => { 47 | if (err1) { 48 | throw err1 49 | } 50 | 51 | // 还是声明交换 52 | const exchange = 'color' 53 | // 声明交换, direct , 不持续 54 | ch.assertExchange(exchange, 'direct', { durable: false }) 55 | 56 | // 接收 direct 类型 下 blue 的消息 57 | ch.assertQueue('', { durable: false }, (err2, q) => { 58 | if (err2) { 59 | throw err2 60 | } 61 | ch.bindQueue(q.queue,exchange,'red') 62 | ch.bindQueue(q.queue,exchange,'blue') 63 | 64 | ch.consume(q.queue,(msg)=>{ 65 | console.log(`--> red/blue收到${exchange} 交换机绑定的队列:${q.queue} 过来的消息:${msg.content.toString()}`) 66 | },{noAck: true}) 67 | }) 68 | // 接收 direct 类型 下 red 的消息 69 | ch.assertQueue('', { durable: false }, (err2, q) => { 70 | if (err2) { 71 | throw err2 72 | } 73 | ch.bindQueue(q.queue,exchange,'blue') 74 | ch.bindQueue(q.queue,exchange,'green') 75 | ch.consume(q.queue,(msg)=>{ 76 | console.log(`--> blue/green 收到${exchange} 交换机绑定的队列:${q.queue} 过来的消息:${msg.content.toString()}`) 77 | },{noAck: true}) 78 | }) 79 | }) 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /src/rpc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-18 14:19:13 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 15:23:51 6 | */ 7 | 8 | const amqp = require('amqplib/callback_api') 9 | 10 | module.exports.service = async () => { 11 | /** 12 | * 1. 声明rpc 队列 13 | * 2. 关闭 ack 每次读取一个消息 14 | * 3. 拿到消息 调用 斐波那契 在发回队列 15 | * 4. 发送回执ack 16 | */ 17 | amqp.connect('amqp://localhost', (err, connection) => { 18 | if (err) { 19 | throw err 20 | } 21 | connection.createChannel((err1, ch) => { 22 | if (err1) { 23 | throw err1 24 | } 25 | const queue = 'rpc_queue' 26 | // 声明队列 27 | ch.assertQueue(queue, { 28 | derable: false 29 | }) 30 | // 每次读取一个消息 31 | ch.prefetch(1) 32 | ch.consume(queue, function reply(msg) { 33 | const n = parseInt(msg.content.toString()) 34 | console.log(`reply: n :: ${n}`) 35 | const r = fibonacci(n) 36 | ch.sendToQueue(msg.properties.replyTo, Buffer.from(r.toString()), { 37 | correlationId: msg.properties.correlationId 38 | }) 39 | ch.ack(msg) 40 | }) 41 | }) 42 | }) 43 | } 44 | 45 | module.exports.client = async () => { 46 | amqp.connect('amqp://localhost', (err, connection) => { 47 | if (err) { 48 | throw err 49 | } 50 | connection.createChannel((err1, ch) => { 51 | if (err1) { 52 | throw err1 53 | } 54 | // 创建队列 55 | ch.assertQueue('',{derable: false},(err2, q) => { 56 | if (err2) { 57 | throw err2 58 | } 59 | const correlationId = generateUuid(); 60 | const num = 5 61 | ch.consume(q.queue,(msg)=>{ 62 | if(msg.properties.correlationId == correlationId){ 63 | console.log(' [.] Got %s', msg.content.toString()) 64 | setTimeout(()=>{ 65 | connection.close() 66 | process.exit(0) 67 | },500) 68 | } 69 | }) 70 | 71 | ch.sendToQueue('rpc_queue',Buffer.from(num.toString()),{ 72 | correlationId: correlationId, 73 | replyTo: q.queue 74 | }) 75 | console.log(`调用完成`) 76 | 77 | } 78 | ) 79 | }) 80 | }) 81 | } 82 | // 斐波那契 83 | function fibonacci(n) { 84 | if (n == 0 || n == 1) { 85 | return n 86 | } else { 87 | console.log(`1--${n-1},2--${n-2}`) 88 | return fibonacci(n - 1) + fibonacci(n - 2) 89 | } 90 | } 91 | 92 | // 模拟uuid 93 | function generateUuid() { 94 | return ( 95 | Math.random().toString() + 96 | Math.random().toString() + 97 | Math.random().toString() 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /src/simplest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-16 20:59:54 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 00:20:39 6 | */ 7 | 8 | // 简单队列 9 | const amqp = require('amqplib/callback_api') 10 | 11 | // 发送消息 12 | module.exports.send = async function() { 13 | /** 14 | * 1.连接mq 15 | * 2.创建通道 16 | * 3.声明队列 17 | * 4.创建消息 18 | * 5.发送消息 19 | */ 20 | // 1. 21 | amqp.connect('amqp://localhost:5672', function(error0, connection) { 22 | if (error0) { 23 | throw error0 24 | } 25 | // 2 26 | connection.createChannel(async function(error1, channel) { 27 | if (error1) { 28 | throw error1 29 | } 30 | // 3. 31 | const queue = 'simplest' 32 | // // 4. 33 | // const msg = 'Hello world' 34 | 35 | channel.assertQueue(queue, { 36 | // 非持久化 37 | durable: false 38 | }) 39 | // 5. 40 | for (let i = 0; i <= 10; i++) { 41 | await new Promise(resolve => { 42 | setTimeout(() => { 43 | channel.sendToQueue(queue, Buffer.from(`生产消息:${i}`)) 44 | console.log(`[p]<-- 生产消息:${i}`) 45 | resolve() 46 | }, 1500) 47 | }) 48 | } 49 | }) 50 | }) 51 | } 52 | 53 | // 接收消息 54 | module.exports.receive = async function() { 55 | /** 56 | * 1.连接mq 57 | * 2.创建通道 58 | * 3.声明队列 59 | * 4.创建回调函数,等待消息 60 | */ 61 | // 1. 62 | amqp.connect('amqp://localhost:5672', function(error0,connection) { 63 | if (error0) { 64 | throw error0 65 | } 66 | // 2 67 | connection.createChannel(function(error1, channel) { 68 | if (error1) { 69 | throw error1 70 | } 71 | var queue = 'simplest' 72 | // 声明队列 73 | channel.assertQueue(queue, { 74 | durable: false 75 | }) 76 | 77 | // 自动回执消息 78 | const opt = { 79 | noAck: true 80 | } 81 | // 每次消费一个消息 82 | channel.prefetch(1) 83 | // 消费消息 84 | channel.consume( 85 | queue, 86 | msg => { 87 | const msgText = msg.content.toString() 88 | console.log(`[c]--> 接收到消息:${msgText}`) 89 | }, 90 | opt 91 | ) 92 | }) 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /src/topic.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-17 23:34:38 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 00:10:52 6 | */ 7 | /* 8 | * @Author: liyonglong 9 | * @Date: 2019-10-17 22:31:47 10 | * @Last Modified by: liyonglong 11 | * @Last Modified time: 2019-10-17 23:09:15 12 | */ 13 | const amqp = require('amqplib/callback_api') 14 | 15 | module.exports.send = () => { 16 | amqp.connect('amqp://localhost:5672', (err0, connection) => { 17 | if (err0) { 18 | throw err 19 | } 20 | // 创建通道 21 | connection.createChannel((err1, ch) => { 22 | if (err1) { 23 | throw err1 24 | } 25 | 26 | // 还是声明交换 27 | const exchange = 'shop' 28 | // 声明交换, topic , 不持续 29 | ch.assertExchange(exchange, 'topic', { autoDelete:true,durable: false }) 30 | 31 | // setInterval(()=>{ 32 | 33 | ch.publish(exchange, 'goods.add', Buffer.from(`商品增加`)) 34 | console.log(`[goods.add] msg 发送完毕`) 35 | 36 | ch.publish(exchange, 'goods.export.send', Buffer.from(`商品出货`)) 37 | console.log(`[goods.export.send] msg 发送完毕`) 38 | 39 | ch.publish(exchange, 'goods.evaluate', Buffer.from(`商品评价`)) 40 | console.log(`[goods.evaluate] msg 发送完毕`) 41 | 42 | ch.publish(exchange, 'goods.addCar', Buffer.from(`商品加购物车`)) 43 | console.log(`[goods.addCar] msg 发送完毕`) 44 | 45 | ch.publish(exchange, 'goods.export.returned', Buffer.from(`商品退货`)) 46 | console.log(`[goods.export.returned] msg 发送完毕`) 47 | 48 | ch.publish(exchange, 'goods.export.change', Buffer.from(`商品换货`)) 49 | console.log(`[goods.export.change] msg 发送完毕`) 50 | // },1000) 51 | }) 52 | }) 53 | } 54 | 55 | module.exports.receive = () => { 56 | amqp.connect('amqp://localhost:5672', (err0, connection) => { 57 | if (err0) { 58 | throw err 59 | } 60 | // 创建通道 61 | connection.createChannel((err1, ch) => { 62 | if (err1) { 63 | throw err1 64 | } 65 | 66 | // 还是声明交换 67 | const exchange = 'shop' 68 | // 声明交换, topic , 不持续 69 | ch.assertExchange(exchange, 'topic', { autoDelete:true,durable: false }) 70 | 71 | // 商品操作队列 72 | ch.assertQueue('', { autoDelete:true,durable: false }, (err2, q) => { 73 | if (err2) { 74 | throw err2 75 | } 76 | ch.bindQueue(q.queue,exchange,'goods.add') 77 | ch.bindQueue(q.queue,exchange,'goods.export.send') 78 | ch.bindQueue(q.queue,exchange,'goods.evaluate') 79 | ch.bindQueue(q.queue,exchange,'goods.addCar') 80 | ch.bindQueue(q.queue,exchange,'goods.add') 81 | 82 | ch.consume(q.queue,(msg)=>{ 83 | console.log(`--> 商品收到${exchange} 交换机绑定的队列:${q.queue} 过来的消息:${msg.content.toString()}`) 84 | }) 85 | },{noAck:true}) 86 | 87 | // 商品售后队列 88 | ch.assertQueue('', { autoDelete:true,durable: false }, (err2, q) => { 89 | if (err2) { 90 | throw err2 91 | } 92 | ch.bindQueue(q.queue,exchange,'goods.export.returned') 93 | ch.bindQueue(q.queue,exchange,'goods.export.change') 94 | ch.consume(q.queue,(msg)=>{ 95 | console.log(`--> 售后 收到${exchange} 交换机绑定的队列:${q.queue} 过来的消息:${msg.content.toString()}`) 96 | }) 97 | },{noAck:true}) 98 | 99 | // 商品进出货统计 100 | ch.assertQueue('', { autoDelete:true,durable: false }, (err2, q) => { 101 | if (err2) { 102 | throw err2 103 | } 104 | ch.bindQueue(q.queue,exchange,'goods.add') 105 | ch.bindQueue(q.queue,exchange,'goods.export.*') 106 | ch.consume(q.queue,(msg)=>{ 107 | console.log(`--> 进出货统计 收到${exchange} 交换机绑定的队列:${q.queue} 过来的消息:${msg.content.toString()}`) 108 | }) 109 | },{noAck:true}) 110 | }) 111 | }) 112 | } 113 | -------------------------------------------------------------------------------- /src/workqueue.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: liyonglong 3 | * @Date: 2019-10-16 20:59:54 4 | * @Last Modified by: liyonglong 5 | * @Last Modified time: 2019-10-18 00:20:59 6 | */ 7 | 8 | // 工作队列,任务 9 | const amqp = require('amqplib/callback_api') 10 | 11 | // 发送消息 12 | module.exports.send = async function() { 13 | /** 14 | * 1.连接mq 15 | * 2.创建通道 16 | * 3.声明队列 17 | * 4.创建消息 18 | * 5.发送消息 19 | */ 20 | // 1. 21 | amqp.connect('amqp://localhost:5672', function(error0, connection) { 22 | if (error0) { 23 | throw error0 24 | } 25 | // 2 26 | connection.createChannel(async function(error1, channel) { 27 | if (error1) { 28 | throw error1 29 | } 30 | // 3. 31 | const queue = 'workqueue' 32 | // // 4. 33 | // const msg = 'Hello world' 34 | 35 | channel.assertQueue(queue, { 36 | durable: false 37 | }) 38 | // 5. 39 | for (let i = 0; i <= 10; i++) { 40 | await new Promise(resolve => { 41 | setTimeout(() => { 42 | channel.sendToQueue(queue, Buffer.from(`生产消息:${i}`)) 43 | console.log(`[p]<-- 生产消息:${i}`) 44 | resolve() 45 | }, 2000) 46 | }) 47 | } 48 | }) 49 | }) 50 | } 51 | 52 | // 接收消息 53 | module.exports.receive = async function(flag, time) { 54 | /** 55 | * 1.连接mq 56 | * 2.创建通道 57 | * 3.声明队列 58 | * 4.创建回调函数,等待消息 59 | */ 60 | // 1. 61 | amqp.connect('amqp://localhost:5672', function(error0, connection) { 62 | if (error0) { 63 | throw error0 64 | } 65 | // 2 66 | connection.createChannel(function(error1, channel) { 67 | if (error1) { 68 | throw error1 69 | } 70 | var queue = 'workqueue' 71 | 72 | channel.assertQueue(queue, { 73 | durable: false 74 | }) 75 | 76 | // 关闭自动回执 77 | const opt = { 78 | noAck: false 79 | } 80 | // 每次消费一个消息 81 | channel.prefetch(1) 82 | // 消费消息 83 | channel.consume( 84 | queue, 85 | msg => { 86 | const msgText = msg.content.toString() 87 | console.log(`[c${flag}]--> 接收到消息:${msgText}`) 88 | setTimeout(() => { 89 | // 手动发送回执 90 | channel.ack(msg) 91 | console.log(`[c${flag}] <--发送回执`) 92 | }, time) 93 | }, 94 | opt 95 | ) 96 | }) 97 | }) 98 | } 99 | --------------------------------------------------------------------------------