如何为发布订阅实例化多个 Redis 连接 (node.js + node_redis)

How to instantiate Multiple Redis Connections for Publish Subscribe (node.js + node_redis)

场景

使用node_redis构建一个简单的Redis发布订阅(聊天)示例:https://github.com/nelsonic/hapi-socketio-redis-chat-example和Hapi.js和 Socket.io)

我们在项目中创建了一个节点模块redis_connection.js(参见:http://git.io/vqaos)来实例化Redis连接,因为我们不想是重复多次连接(到RedisCloud)的代码:

var redis = require('redis');
var url   = require('url');
var redisURL    = url.parse(process.env.REDISCLOUD_URL);
var redisClient = redis.createClient(redisURL.port, redisURL.hostname,
                  {no_ready_check: true});
redisClient.auth(redisURL.auth.split(":")[1]);

module.exports = redisClient;

然后我们像这样使用

var redisClient = require('./redis_connection.js');

// Confirm we are able to connect to  RedisCloud:
redisClient.set('redis', 'working', redisClient.print);
redisClient.get('redis', function (err, reply) {
  console.log('RedisCLOUD is ' +reply.toString());
});

这适用于正常的 GET/SET Redis 操作, 但是当我们尝试实例化到 Redis 的多个连接时(例如:一个用于发布,另一个用于订阅,第三个仅用于 GET/SET keys/values),我们得到一个错误:

问题

我们看到以下错误:

Error: Connection in subscriber mode, only subscriber commands may be used

我们做错了什么?

我们看到此问题时的完整代码:http://git.io/vqa6y

备注

我们尝试挖掘现有的 SO Q/A,例如:

但没有找到完全符合我们情况的解决方案...

(任何suggestions/help非常感谢!)

问题是您的 redis 客户端创建代码正在被 requires 缓存,因此您一次又一次地重复使用相同的连接。您可以 return 一个函数,而不是 return 在您的 redis_connection 模块中建立连接:

module.exports = function(){
    var redis = require('redis'); 
    var url = require('url'); var redisURL = url.parse(process.env.REDISCLOUD_URL); 
    var redisClient = redis.createClient(redisURL.port, redisURL.hostname, {no_ready_check: true});
    redisClient.auth(redisURL.auth.split(":")
    return redisClient;
}

然后这样称呼它:

var redisClient = require('./redis_connection.js')();

未测试,但评论时间太长。

尝试定义另一个 redis 连接模块,一个用于您的常规使用,另一个仅用于您的 pubsub 订阅使用。

向您的项目添加 redis_pubsub_connection.js

var redis = require('redis');
var url   = require('url');
var redisURL    = url.parse(process.env.REDISCLOUD_URL);
var redisPubSubClient = redis.createClient(redisURL.port, redisURL.hostname,
                  {no_ready_check: true});
redisPubSubClient.auth(redisURL.auth.split(":")[1]);

module.exports = redisPubSubClient;

并将您的 publish.js 要求语句更改为:

var redis = require('./redis_pubsub_connection'); // RedisCloud

如果你想提供常规连接和子连接,并且你想确保你在整个应用程序中只有一个,那么你可以使用包括单例概念的两种解决方案的组合,比如这个:

var subConnection, con;

var createConnection = module.exports.createConnection = function(){
    var redis = require('redis'); 
    var url = require('url'); var redisURL = url.parse(process.env.REDISCLOUD_URL); 
    var redisClient = redis.createClient(redisURL.port, redisURL.hostname, {no_ready_check: true});
    redisClient.auth(redisURL.auth.split(":")
    return redisClient;
}

module.exports.getSubConnection = function(){

if (!subConnection) 
    subConnection = createConnection();

return subConnection
}

module.exports.getConnection = function(){

if (!con) 
    con = createConnection();

return con
}

}

重复其他两种连接类型并像这样称呼它

var con =  require('./redis_connection.js').getConnection();

redis-连接node.js模块

为了在我们的项目中保持这个可重复使用,我们编写了一个 (mini) node.js 模块来初始化 Redis 连接:https://github.com/dwyl/redis-connection

代码简单且经过测试,并在需要时负责身份验证。 (不要在此处复制粘贴模块以避免重复
参见:https://github.com/dwyl/redis-connection/blob/master/index.js

用法:

从 NPM 安装

npm install redis-connection --save

在你的脚本中使用

var redisClient = require('redis-connection')();
redisClient.set('hello', 'world');
redisClient.get('hello', function (err, reply) {
  console.log('hello', reply.toString()); // hello world
});

发布订阅

var redisClient = require('redis-connection')(); // Publisher
var redisSub = require('redis-connection')('subscriber');
redisSub.subscribe("chat:messages:latest", "chat:people:new");

有关工作示例,请参阅:https://github.com/dwyl/hapi-socketio-redis-chat-example

优点是我们可以在同一项目中的多个文件中重复使用相同的 redisClient 而无需创建新连接(单个或 pub/sub 连接被缓存并且重新使用)

信用:我们从几个地方借鉴了想法,所以对所有答案都投了赞成票。但最终我们编写了一个略有不同的解决方案,因此我们在 NPM/GitHub 上与大家分享了它。再次感谢大家!