16

写在开头

  • 最近的Devops和微前端已经写得差不多,开始复习下后端相关知识,之前想写的这篇文章,终于落地
  • 如果你想加入前端交流群,可以文末联系我加入

正式开始

  • 电脑环境 推荐Mac|Linux
  • 安装redis,并且启动redis
`redis-server` 
  • 启动成功后会如下所示:

  • redis默认端口6379

开始写Node.js代码

  • 下载redis这个库
yarn add redis --save
  • 使用Node.js连接redis
const redis = require('redis');

const client = redis.createClient(6379, '127.0.0.1'); 
  • 由于是消息队列,于是需要有一个生产者、消费者

这里普及下消息队列的使用,跟redis一样,都是属于进程外的服务,就是单独要占用一个端口起服务的

什么是消息队列?

  • “消息队列”是在消息的传输过程中保存消息的容器。
  • 消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。
  • 即有生产者,消费者,发布订阅模式实现

消息队列使用场景

  • 业务解耦
  • 异步处理提升性能

  • 限流削峰(降低成本,不可能按流量最高峰去配备服务器)

开始实现

  • 生产者
`const redis = require('redis');

const client = redis.createClient(6379, '127.0.0.1');

client.on('error', function (err) {
  console.log('err' + err);
});

client.on('ready', function () {
  client.publish('testFirst', 'hi! first!');
  client.publish('testSecond', 'hi! second!');
  client.publish('message', 'hi! message!');
});` 
  • 生产者对特定的channel进行publish,并且附带参数
  • 消费者订阅特定的channel,消费,并且获取数据
`const client = require('redis').createClient(6379, '127.0.0.1');

client.on('error', function (err) {
  console.log('err' + err);
});

client.subscribe('testSecond');
client.subscribe('message');
client.on('subscribe', function (channel, count) {
  console.log('subscribe channel:' + channel + ', count:' + count);
});
client.on('message', function (channel, message) {
  console.log('message channel:' + channel + ', msg:' + message);
});
client.on('unsubscribe', function (channel, count) {
  console.log('unsubscribe channel:' + channel + ', count:' + count);
});` 
  • 结果:

  • 我订阅了testsecoud和message两个通道,于是触发了subscribe事件两次,符合预期

模拟场景,生产者不断提供生产

  • 加入定时器
`const redis = require('redis');

const client = redis.createClient(6379, '127.0.0.1');

client.on('error', function (err) {
  console.log('err' + err);
});

client.on('ready', function () {
  setInterval(() => {
    client.publish('testSecond', 'hi! second!');
    client.publish('message', 'hi! message!');
  },1000);
});` 
  • 此时消费者不断打印,触发了message事件

这样,我们使用redis发布订阅模式,实现了简单的消息队列

实现流量削峰,限流

  • 目前我们生产是1S一条消息,但是我想控制成2S消费一次,可以吗?
  • 我们控制下消费频率,首先不改变生产频率
`const client = require('redis').createClient(6379, '127.0.0.1');
const ArrayList = [];
client.on('error', function (err) {
  console.log('err' + err);
});
client.subscribe('testSecond');
client.subscribe('message');
client.on('subscribe', function (channel, count) {
  console.log('subscribe channel:' + channel + ', count:' + count);
});
client.on('message', function (channel, message) {
  ArrayList.push({ channel, message });
});
client.on('unsubscribe', function (channel, count) {
  console.log('channel:' + channel + ', count:' + count);
});

setInterval(()=>{
    console.log(ArrayList,'ArrayList')
},2000)` 
  • 每2S读取一次队列的数据

  • 模拟的跟实际有什么不一样?

    • 模拟的是在一个进程端口内,属于进程内缓存
    • 真实的是可以通过回复ACK确认消费,独占一个端口进程,属于进程外缓存

一个简单的通过redis实现消息队列就完成了

  • 源码地址:https://github.com/JinJieTan/MQ

PeterTan
14.4k 声望30k 粉丝