<commit_before>---
author: 小莫
date: 2021-05-14
title: nodejs中使用redis的订阅分发功能
tags:
- 工具
category: technology
permalink: redisPubSub

---
订阅者模式是23中设计模式中常用的模式之一，我们经常会在代码中用到。那么redis的订阅和分发有什么作用呢？当应用同时有多个实例时，如果调用某个api，那么通过负载均衡请求只会到达某一个实例，但是如果我们有需求调用api时所有的实例都需要对应的处理，那要怎么实现呢？


<!-- more -->
# pub/sub的原理
应用在启动时可以通过subscribe来订阅一个或多个channel,这就好比我们通过RSS订阅某个或者多个喜欢的博主，那么此是我们对这个channel就处于监听状态。当博主发布新的文章的话就会给所有的订阅者发送消息，我们监听到发过来的消息后就可以做对应的处理。

# nodejs redis库
![image-20210517123635837](https://image.xiaomo.info//blog/image-20210517123635837.png)

通过关键字搜索排名前三的依次是`redis`,`connect-redis`,`ioredis`，`redis`发布最早当然积累的人气也最高，但它的方法比较古老，leader推荐使用`ioredis`。不过经过实际尝试，不同的库写法基本上没有区别，互相切换也就只需要换一个依赖库，不到3分钟即可解决。

1. [node-redis](https://github.com/NodeRedis/node-redis)
    注意：这个库在github名字和npm托管上不一样,npm上直接叫`redis`。

  ```js
  const redis = require("redis");
  
  const subscriber = redis.createClient(port,host);
  const publisher = redis.createClient(port,host);
  
  let messageCount = 0;
  
  subscriber.on("subscribe", function(channel, count) {
    publisher.publish("a channel", "a message");
    publisher.publish("a channel", "another message");
  });
  
  subscriber.on("message", function(channel, message) {
    messageCount += 1;
  
    console.log("Subscriber received message in channel '" + channel + "': " + message);
  
    if (messageCount === 2) {
      subscriber.unsubscribe();
      subscriber.quit();
      publisher.quit();
    }
  });
  
  subscriber.subscribe("a channel");
  ```

2. [ioreids](https://www.npmjs.com/package/ioredis)

  注意：这里官方给例子分了2个文件`publisher.js`和`subscriber.js`2个文件，这里把合并在了一起。

```js
const Redis = require("ioredis");
const pub = new Redis(port,host);
const sub = new Redis(port,host);

setInterval(() => {
  const message = { foo: Math.random() };
  // Publish to my-channel-1 or my-channel-2 randomly.
  const channel = `my-channel-${1 + Math.round(Math.random())}`;

  // Message can be either a string or a buffer
  pub.publish(channel, JSON.stringify(message));
  console.log("Published %s to %s", message, channel);
}, 1000);

sub.subscribe("my-channel-1", "my-channel-2", (err, count) => {
  if (err) {
    // Just like other commands, subscribe() can fail for some reasons,
    // ex network issues.
    console.error("Failed to subscribe: %s", err.message);
  } else {
    // `count` represents the number of channels this client are currently subscribed to.
    console.log(
      `Subscribed successfully! This client is currently subscribed to ${count} channels.`
    );
  }
});

sub.on("message", (channel, message) => {
  console.log(`Received ${message} from ${channel}`);
});

// There's also an event called 'messageBuffer', which is the same as 'message' except
// it returns buffers instead of strings.
// It's useful when the messages are binary data.
sub.on("messageBuffer", (channel, message) => {
  // Both `channel` and `message` are buffers.
  console.log(channel, message);
});
```



# 主要API

-   publish        给某个channel发布一条消息，一般是通过用户或者程序主动触发。
-   subscribe    订阅某个或多个channel，一般来说应用启动时就会将需要的channel进行订阅。
-   on                 监听某个或多个channel的消息，一旦publish了对应channel的消息，On就会收到然后做对应的业务逻辑处理。



# 总结

不难发现，`redis`的示例中创建了2个redis实例，分别为`publisher`和`subscriber`，而`ioredis`中虽然也是创建了2个redis实例，但是它分成了2个文件，名字者叫redis。因此当对redis实现原理不书仅仅按照api使用的情况下很容易像我一样只创建一个实例，然后用同一个实例进行pub/sub/on。这样做的后果是只会pub,sub中收不到任何消息， 实际上pub/sub需要分别创建单独的实例进行处理。<commit_msg>修改错别字<commit_after>---
author: 小莫
date: 2021-05-14
title: nodejs中使用redis的订阅分发功能
tags:
- 工具
category: technology
permalink: redisPubSub

---
订阅者模式是23中设计模式中常用的模式之一，我们经常会在代码中用到。那么redis的订阅和分发有什么作用呢？当应用同时有多个实例时，如果调用某个api，那么通过负载均衡请求只会到达某一个实例，但是如果我们有需求调用api时所有的实例都需要对应的处理，那要怎么实现呢？


<!-- more -->
# pub/sub的原理
应用在启动时可以通过subscribe来订阅一个或多个channel,这就好比我们通过RSS订阅某个或者多个喜欢的博主，那么此是我们对这个channel就处于监听状态。当博主发布新的文章的话就会给所有的订阅者发送消息，我们监听到发过来的消息后就可以做对应的处理。

# nodejs redis库
![image-20210517123635837](https://image.xiaomo.info//blog/image-20210517123635837.png)

通过关键字搜索排名前三的依次是`redis`,`connect-redis`,`ioredis`，`redis`发布最早当然积累的人气也最高，但它的方法比较古老，leader推荐使用`ioredis`。不过经过实际尝试，不同的库写法基本上没有区别，互相切换也就只需要换一个依赖库，不到3分钟即可解决。

1. [node-redis](https://github.com/NodeRedis/node-redis)
    注意：这个库在github名字和npm托管上不一样,npm上直接叫`redis`。

  ```js
  const redis = require("redis");
  
  const subscriber = redis.createClient(port,host);
  const publisher = redis.createClient(port,host);
  
  let messageCount = 0;
  
  subscriber.on("subscribe", function(channel, count) {
    publisher.publish("a channel", "a message");
    publisher.publish("a channel", "another message");
  });
  
  subscriber.on("message", function(channel, message) {
    messageCount += 1;
  
    console.log("Subscriber received message in channel '" + channel + "': " + message);
  
    if (messageCount === 2) {
      subscriber.unsubscribe();
      subscriber.quit();
      publisher.quit();
    }
  });
  
  subscriber.subscribe("a channel");
  ```

2. [ioreids](https://www.npmjs.com/package/ioredis)

  注意：这里官方给例子分了2个文件`publisher.js`和`subscriber.js`2个文件，这里把合并在了一起。

```js
const Redis = require("ioredis");
const pub = new Redis(port,host);
const sub = new Redis(port,host);

setInterval(() => {
  const message = { foo: Math.random() };
  // Publish to my-channel-1 or my-channel-2 randomly.
  const channel = `my-channel-${1 + Math.round(Math.random())}`;

  // Message can be either a string or a buffer
  pub.publish(channel, JSON.stringify(message));
  console.log("Published %s to %s", message, channel);
}, 1000);

sub.subscribe("my-channel-1", "my-channel-2", (err, count) => {
  if (err) {
    // Just like other commands, subscribe() can fail for some reasons,
    // ex network issues.
    console.error("Failed to subscribe: %s", err.message);
  } else {
    // `count` represents the number of channels this client are currently subscribed to.
    console.log(
      `Subscribed successfully! This client is currently subscribed to ${count} channels.`
    );
  }
});

sub.on("message", (channel, message) => {
  console.log(`Received ${message} from ${channel}`);
});

// There's also an event called 'messageBuffer', which is the same as 'message' except
// it returns buffers instead of strings.
// It's useful when the messages are binary data.
sub.on("messageBuffer", (channel, message) => {
  // Both `channel` and `message` are buffers.
  console.log(channel, message);
});
```



# 主要API

-   publish        给某个channel发布一条消息，一般是通过用户或者程序主动触发。
-   subscribe    订阅某个或多个channel，一般来说应用启动时就会将需要的channel进行订阅。
-   on                 监听某个或多个channel的消息，一旦publish了对应channel的消息，On就会收到然后做对应的业务逻辑处理。



# 总结

不难发现，`redis`的示例中创建了2个redis实例，分别为`publisher`和`subscriber`，而`ioredis`中虽然也是创建了2个redis实例，但是它分成了2个文件，名字者叫redis。因此当对redis实现原理不熟仅仅按照api使用的情况下很容易像我一样只创建一个实例，然后用同一个实例进行pub/sub/on。这样做的后果是只会pub,sub中收不到任何消息， 实际上pub/sub需要分别创建单独的实例进行处理。
{'fraction_non_alphanumeric': 0.13994082840236685, 'fraction_numerical': 0.01893491124260355, 'mean_word_length': 4.71513102282333, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7250770', 'n_tokens_mistral': 3137, 'n_tokens_neox': 3270, 'n_words': 437}