Node.js连接RabbitMQ,断线重连,动态绑定routing key
RabbitMQ官方提供的教程https://www.rabbitmq.com/tuto...,是基于回调的。
下面将给出基于Promise式的写法。并且实现动态的队列绑定
初始化配置
const amqp = require('amqplib')
// rabbitMQ地址
const {amqpAddrHost} = require('../config/index.js')
// 交换机名称
const ex = 'amq.topic'
const amqpAddr = `amqp://${amqpAddrHost}`
// 读取HOSTNAME, 在跑多实例时,例如在k8s中,HOSTNAME可以获取当前pod的名称
// 多实例时,写日志,或者建立连接时,最好带上pod名称,如果出现问题,也比较好定位哪个pod出现的问题。
const hostName = process.env.HOSTNAME
// 队列的属性设置
// 一般来说,最好设置队列自动删除autoDelete,当链接断开时,队列也会删除,这样不会产生非常多的无用队列
// durable是用来的持久化的,最好也可以设置成不持久化
const queueAttr = {autoDelete: true, durable: false}
// 定义channel的引用,当链接建立时,所有方法都可以通过引用CH来获取channel方法
let CH = null
向队列发送消息的函数
// 向队列发送消息的函数
function publishMessage (msg) {
  if (!CH) {
    return ''
  }
  msg = JSON.stringify(msg)
  // 指定交换机ex, routing key, 以及消息的内容
  CH.publish(ex, eventBusTopic, Buffer.from(msg))
}
当链接rabbitMQ断开时,要主动去重连
function reconnectRabbitMq () {
  log.info('reconnect_rabbit_mq')
  connectRabbitMq()
}
连接rabbitMQ的主要函数
function connectRabbitMq () {
  amqp.connect(amqpAddr, {
    // 设置connection_name的属性,可以在rabbitMQ的控制台的UI上,看到连接是来自哪个实例
    clientProperties: {
      connection_name: hostName
    }
  })
  .then((conn) => {
    log.info('rabbitmq_connect_successd')
    // 一定要加上链接的报错事件处理,否则一旦报error错,如果不处理这个错误,程序就会崩溃
    // error是个特别的事件,务必要处理的
    // 报错就直接去重连
    conn.on('error', (err) => {
      log.error('connect_error ' + err.message, err)
      reconnectRabbitMq()
    })
    // 创建channel
    return conn.createChannel()
  })
  .then((ch) => {
    CH = ch
    // 初始化交换机
    ch.assertExchange(ex, 'topic', {durable: true})
    // 初始化一个队列,队列名就用hostName, 比较容易从对列名上知道是哪个实例创建的队列
    return ch.assertQueue(hostName, queueAttr)
  })
  .then((q) => {
    // 可以在队列初始化完毕就立即绑定routing key, 也可以暂时不绑定,后续动态的绑定
    // CH.bindQueue(q.queue, ex, 'some.topic.aaa')
    // 消费者,获取消息
    CH.consume(q.queue, (msg) => {
      var _msg = msg.content.toString()
      var MSG = JSON.parse(_msg)
      log.info(_msg, MSG)
    }, {noAck: true})
  })
  .catch((err) => {
    console.log(err)
  })
}
动态给队列绑定或者解绑routing key
function toggleBindQueue (routingKey, bind) {
  return new Promise((resolve, reject) => {
    if (!CH) {
      log.error('channel not established')
      reject(new Error('channel not established'))
      return ''
    }
    // 初始化队列,如果队列已经存在,就会直接使用
    CH.assertQueue(`${hostName}`, queueAttr)
    .then((q) => {
      // 如果bind是true,就绑定。否则就解绑
      if (bind) {
        log.info(`bindQueue ${hostName} ${topic}`)
        return CH.bindQueue(q.queue, ex, topic)
      } else {
        return CH.unbindQueue(q.queue, ex, topic)
      }
    })
    .then((res) => {
      resolve()
    })
    .catch((err) => {
      reject(err)
      log.error(err)
    })
  })
}
module.exports = {
  connectRabbitMq,
  toggleBindQueue,
  publishMessage
}
使用方法
加入你的服务端用的是Express, 那么在app.js中可以
...
const {connectRabbitMq} = require('./connect-mq.js')
connectRabbitMq()
...
完整代码
// onnect-mq.js
const amqp = require('amqplib')
// rabbitMQ地址
const {amqpAddrHost} = require('../config/index.js')
// 交换机名称
const ex = 'amq.topic'
const amqpAddr = `amqp://${amqpAddrHost}`
// 读取HOSTNAME, 在跑多实例时,例如在k8s中,HOSTNAME可以获取当前pod的名称
// 多实例时,写日志,或者建立连接时,最好带上pod名称,如果出现问题,也比较好定位哪个pod出现的问题。
const hostName = process.env.HOSTNAME
// 队列的属性设置
// 一般来说,最好设置队列自动删除autoDelete,当链接断开时,队列也会删除,这样不会产生非常多的无用队列
// durable是用来的持久化的,最好也可以设置成不持久化
const queueAttr = {autoDelete: true, durable: false}
// 定义channel的引用,当链接建立时,所有方法都可以通过引用CH来获取channel方法
let CH = null
// 向队列发送消息的函数
function publishMessage (msg) {
  if (!CH) {
    return ''
  }
  msg = JSON.stringify(msg)
  // 指定交换机ex, routing key, 以及消息的内容
  CH.publish(ex, eventBusTopic, Buffer.from(msg))
}
// 当链接rabbitMQ断开时,要主动去重连
function reconnectRabbitMq () {
  log.info('reconnect_rabbit_mq')
  connectRabbitMq()
}
// 链接rabbitMQ的主要函数
function connectRabbitMq () {
  amqp.connect(amqpAddr, {
    // 设置connection_name的属性,可以在rabbitMQ的控制台的UI上,看到链接是来自哪个实例
    clientProperties: {
      connection_name: hostName
    }
  })
  .then((conn) => {
    log.info('rabbitmq_connect_successd')
    // 一定要加上链接的报错事件处理,否则一旦报error错,如果不处理这个错误,程序就会崩溃
    // error是个特别的事件,务必要处理的
    // 报错就直接去重连
    conn.on('error', (err) => {
      log.error('connect_error ' + err.message, err)
      reconnectRabbitMq()
    })
    // 创建channel
    return conn.createChannel()
  })
  .then((ch) => {
    CH = ch
    // 初始化交换机
    ch.assertExchange(ex, 'topic', {durable: true})
    // 初始化一个队列,队列名就用hostName, 比较容易从对列名上知道是哪个实例创建的队列
    return ch.assertQueue(hostName, queueAttr)
  })
  .then((q) => {
    // 可以在队列初始化完毕就立即绑定routing key, 也可以暂时不绑定,后续动态的绑定
    // CH.bindQueue(q.queue, ex, 'some.topic.aaa')
    // 消费者,获取消息
    CH.consume(q.queue, (msg) => {
      var _msg = msg.content.toString()
      var MSG = JSON.parse(_msg)
      log.info(_msg, MSG)
    }, {noAck: true})
  })
  .catch((err) => {
    console.log(err)
  })
}
// 动态给队列绑定或者解绑routing key
function toggleBindQueue (routingKey, bind) {
  return new Promise((resolve, reject) => {
    if (!CH) {
      log.error('channel not established')
      reject(new Error('channel not established'))
      return ''
    }
    // 初始化队列,如果队列已经存在,就会直接使用
    CH.assertQueue(`${hostName}`, queueAttr)
    .then((q) => {
      // 如果bind是true,就绑定。否则就解绑
      if (bind) {
        log.info(`bindQueue ${hostName} ${topic}`)
        return CH.bindQueue(q.queue, ex, topic)
      } else {
        return CH.unbindQueue(q.queue, ex, topic)
      }
    })
    .then((res) => {
      resolve()
    })
    .catch((err) => {
      reject(err)
      log.error(err)
    })
  })
}
module.exports = {
  connectRabbitMq,
  toggleBindQueue,
  publishMessage
}
来源:https://segmentfault.com/a/1190000016807727
Node.js连接RabbitMQ,断线重连,动态绑定routing key的更多相关文章
- Node.js使用rabbitMQ(一)
		
目前使用的开发环境主要还是win,所以也只是介绍在win下的rabbitMQ的使用. 一.安装rabbitMQ(Installing on Windows) 关于安装地址,参见:Installing ...
 - Node.js连接Mysql,并把连接集成进Express中间件中
		
引言 在node.js连接mysql的过程,我们通常有两种连接方法,普通连接和连接池. 这两种方法较为常见,当我们使用express框架时还会选择使用中间express-myconnection,可以 ...
 - Node.js 连接 MySQL 并进行数据库操作
		
Node.js 连接 MySQL 并进行数据库操作 按照这篇操作mysql的指引,我远程操作了我另一台电脑的mysql数据库. var mysql = require('mysql'); var c ...
 - Node.js连接MySQL数据库及构造JSON的正确姿势
		
做一下整理,以前也很随意的引入包链接数据库,后来发现常常连接出问题,异常退出,后来使用在网上一个方法解决问题,网址由于书签丢失,抱歉不能引用了.再有就是简单的模块化下,使得目录合理点,再有就是说明一下 ...
 - node.js连接MySQL操作及注意事项
		
node.js作为服务端的js运行环境已经出现了有几年了,最近我有个朋友也在做这方面的开发,但是也是刚刚接触,遇到了很多坑.前几天他们在操作数据库的时候出现了点问题,后来我们一起看了看,其实都是nod ...
 - 【js学习】js连接RabbitMQ达到实时消息推送
		
js连接RabbitMQ达到实时消息推送 最近在自己捯饬一个网站,有一个功能是需要后端处理完数据把数据发布到MQ中,前端再从MQ中接收数据.但是前端连接MQ又成了一个问题,在网上搜了下资料,点进去一篇 ...
 - Windows 7 下 Node.js 连接 Oracle
		
原创作者: sailtseng 1. 安装 Oracle 11g express 详见: <Windows 7 x64 安装 Oracle 11g Express> 2. 安装 Micr ...
 - windows下安装mongodb以及node.js连接mongodb
		
一.MongoDB 下载 下载地址 https://www.mongodb.com/download-center#community 选择windows版下载,然后安装. 二.安装完毕后创建数据 ...
 - node.js连接本地数据库及json返回数据
		
新建一个文件夹node.js,目录下打开命令初始化一下 cnpm init 然后下载express框架 cnpm install express --save 接着下载数据库的依赖 cnpm inst ...
 
随机推荐
- 学习经常遇到的浮动(float)
			
参考自 小辉随笔: https://www.cnblogs.com/lchsirblog/p/9582989.html 一.什么时候需要使用浮动 最常见的情景是:多个块级元素(如div)需要同一行显示 ...
 - Mybatis-Plus和Mybatis的区别
			
1.List item 区别一如果Mybatis Plus是扳手,那Mybatis Generator就是生产扳手的工厂.通俗来讲——MyBatis:一种操作数据库的框架,提供一种Mapper类,支持 ...
 - linux下为已经编译好的php环境添加mysql扩展(php安装完成后如何添加mysql扩展)
			
问题背景 平常我们都是先安装mysql,然后才能去安装php.假如先安装php,后安装mysql,由于php需要连接mysql,因而在php引擎中需要配置使用mysql.so扩展.这时需要手动编译生成 ...
 - web下载文件夹
			
1.文件下载有两种方式:一种是超链接,一种是Servlet提供下载. 2.超链接下载时:当文件可以在网页直接打开时,会直接打开文件,而不是下载,当文件打开不了时,会提供下载窗口. 3.超链接下载原理 ...
 - BZOJ 4517: [Sdoi2016]排列计数 错排 + 组合
			
从 $n$ 个数中选 $m$ 个不错排,那就是说 $n-m$ 个数是错排的. 用组合数乘一下就好了. Code: #include <cstdio> #include <algori ...
 - djangle中模板系统的使用
			
django相关的命令行命令: 创建一个djaongo的应用:在已经创建号的应用文件夹中运行:django-admin.py startproject projectName 开启系统自带的服务器在网 ...
 - SOUI中对象的生命周期管理
			
C++程序员最难的一环就是处理内存泄漏. 很多情况下,一个对象在一个模块里分配了内存,忘记了释放,或者在另一个模块里释放都会导致内存相关的问题. SOUI中大部分暴露在应用层的对象都使用类似COM的引 ...
 - 1、Shiro简介以及整体架构
			
1.Shiro概念和作用: 利用shiro可以快速完成权限管理模块的开发 Spring的官网也是用Shiro做安全管理的... Shiro整体架构: 可能你感觉上面的图片很乱,但是你一定要先大体有个印 ...
 - 歌手详情数据处理和Song类的封装
			
我们现在每首歌曲的数据都是这样的 我们需要在这个数据里面去提取我们需要的部分,来构造成我们需要的数据对象 那我们要和创建singer.js一样 同样也要创建song.js类 我们还要获取到每首歌对应 ...
 - set_option()函数
			
这个函数用于设置dataframe的输出显示, import pandas as ps pd.set_option('expand_frame_repr', True) # True就是可以换行显示. ...