使用Node.js驅動Redis,實現一個訊息佇列!

Peter譚金傑發表於2020-06-17

寫在開頭

  • 最近的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

相關文章