Node.js real redis和Heroku redis插件之间的差异

Node.js real redis和Heroku redis插件之间的差异,node.js,heroku,redis,Node.js,Heroku,Redis,在Heroku上运行代码末尾显示的代码时,我发现程序行为的主要差异取决于我使用的是redis服务器还是Heroku redis插件(RedisCloud和RedisToGo均未成功尝试) 测试代码是通过primus rooms从Redis PubSub到websockets的发射器 首先,我将展示预期的程序行为 我的REDISLOCAL_URL环境变量指向运行redis 2.8.6(与RedisCloud版本相同)的我自己的redis服务器 在日志中,我看到: {"name":"index","

在Heroku上运行代码末尾显示的代码时,我发现程序行为的主要差异取决于我使用的是redis服务器还是Heroku redis插件(RedisCloud和RedisToGo均未成功尝试)

测试代码是通过primus rooms从Redis PubSub到websockets的发射器

首先,我将展示预期的程序行为

我的REDISLOCAL_URL环境变量指向运行redis 2.8.6(与RedisCloud版本相同)的我自己的redis服务器

在日志中,我看到:

{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":30,"msg":"Server running on port 19690","time":"2014-04-27T16:38:38.706Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Redis Sub ready on ckir.homeip.net","time":"2014-04-27T16:38:39.226Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Redis Pub ready on ckir.homeip.net","time":"2014-04-27T16:38:39.225Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Redis Pub authenticated","time":"2014-04-27T16:38:39.224Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Redis Sub authenticated","time":"2014-04-27T16:38:39.226Z","v":0}
此时,我连接了一个客户端(外部浏览器)。一切都很好,我可以在浏览器上看到时间的滴答声

{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client connected id: 1398617743234$0","time":"2014-04-27T16:55:43.236Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client subscribed to time, 1 total subscriptions","time":"2014-04-27T16:55:43.595Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client connected id: 1398617858982$0","time":"2014-04-27T16:57:38.985Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client subscribed to time, 1 total subscriptions","time":"2014-04-27T16:57:39.150Z","v":0}
此时,我在浏览器上按“重新加载”。程序按预期断开连接并取消订阅,然后重新连接并再次订阅。无论我按了多少次“重新加载”,行为都保持不变

{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client disconnected id: 1398617743234$0","time":"2014-04-27T16:56:06.995Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client unsubscribed from time, 0 total subscriptions","time":"2014-04-27T16:56:07.160Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client connected id: 1398617768295$1","time":"2014-04-27T16:56:08.298Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client subscribed to time, 1 total subscriptions","time":"2014-04-27T16:56:08.630Z","v":0}
此时,我关闭浏览器,因此不会发生重新连接

{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client disconnected id: 1398617768295$1","time":"2014-04-27T16:56:31.229Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client unsubscribed from time, 0 total subscriptions","time":"2014-04-27T16:56:31.393Z","v":0}
在这一点上,我输入一个“heroku config unset REDISLOCAL_URL”,这样heroku重新启动dyno,我看到了

Redis connections closed
这次我将使用RedisCloud重复上述步骤

我们开局不错。连接良好

{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":30,"msg":"Server running on port 35323","time":"2014-04-27T16:57:17.215Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Redis Sub ready on pub-redis-10891.us-east-1-2.1.ec2.garantiadata.com","time":"2014-04-27T16:57:17.242Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Redis Pub ready on pub-redis-10891.us-east-1-2.1.ec2.garantiadata.com","time":"2014-04-27T16:57:17.243Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Redis Sub authenticated","time":"2014-04-27T16:57:17.237Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Redis Pub authenticated","time":"2014-04-27T16:57:17.242Z","v":0}
此时,我连接了一个客户端(外部浏览器)。一切都很好,我可以在浏览器上看到时间的滴答声

{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client connected id: 1398617743234$0","time":"2014-04-27T16:55:43.236Z","v":0}
{"name":"index","hostname":"04580326-9e33-4829-bf89-301e10f85fe7","pid":2,"level":20,"msg":"Client subscribed to time, 1 total subscriptions","time":"2014-04-27T16:55:43.595Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client connected id: 1398617858982$0","time":"2014-04-27T16:57:38.985Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client subscribed to time, 1 total subscriptions","time":"2014-04-27T16:57:39.150Z","v":0}
我在浏览器上按“重新加载”,问题出在这里。不再重新连接和重新订阅,也不再有数据进入浏览器屏幕

{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client disconnected id: 1398617858982$0","time":"2014-04-27T16:57:58.560Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client unsubscribed from time, 0 total subscriptions","time":"2014-04-27T16:57:58.561Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client connected id: 1398617879988$1","time":"2014-04-27T16:57:59.989Z","v":0}
{"name":"index","hostname":"97617fd4-4224-4c3f-a638-3b9f6840fdb7","pid":2,"level":20,"msg":"Client disconnected id: 1398617879988$1","time":"2014-04-27T16:58:13.798Z","v":0}
我需要重新启动dyno以使浏览器客户端重新工作。你知道为什么会这样吗?有什么可行的解决办法吗

下面是我与package.json文件一起使用的代码,以获得更好的文档

'use strict';
//
// Program exit handlers to close redis connections
//
var Sync = require('sync');
process.stdin.resume(); //so the program will not close instantly

function connectionsclose() {
    redisPub.end();
    redisSub.end();
    console.log("Redis connections closed");
    process.exit();
}

function exitHandler(options, err) {
    if (options.cleanup) {
        Sync(function() {
            connectionsclose();
        });
    }
    if (err) {
        Sync(function() {
            connectionsclose();
        });
    }
    if (options.exit) {
        process.exit();
    };
}

//do something when app is closing
process.on('exit', exitHandler.bind(null, {
    exit: true
}));

//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {
    cleanup: true
}));
process.on('SIGTERM', exitHandler.bind(null, {
    cleanup: true
}));

//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {
    cleanup: true
}));

//
// Start logging
//
var path = require('path');
var bunyan = require('bunyan');

var port = process.env.PORT || 3000;
var basename = path.basename(__filename, '.js');
var log = bunyan.createLogger({
    name: basename,
    streams: [{
        level: 'debug',
        stream: process.stdout
    }, {
        level: 'info',
        path: path.resolve('./logs/' + basename + '.log'),
    }]
});

//
// Do your express magic.
//
var express = require('express');
var app = express();

app.set('port', port);
app.use(express.static(__dirname + '/public'));
app.get('/keepalive', function(req, res) {
    res.send('OK');
});

//
// Create Primus server
//
var http = require('http');
var Primus = require('primus');

var server = http.createServer(app);

var primus = new Primus(server, {
    transformer: 'websockets',
    parser: 'JSON',
});

primus.save(__dirname + '/public/primus.js');

primus.on('connection', function(spark) {

    log.debug("Client connected id: " + spark.id);

    spark.on('data', function(data) {
        data = data || {};
        log.trace("Client id: " + spark.id + " message: " + JSON.stringify(data));

        switch (data.command) {
            case 'join':
                redisSub.subscribe(data.room);
                break;
            case 'leave':
                redisSub.unsubscribe(data.room);
                break;
            default:
                log.debug("Client " + spark.id + " wrote " + JSON.stringify(message));
        }

    });

});

primus.on('disconnection', function(spark) {
    log.debug("Client disconnected id: " + spark.id);
    // unsubscribe from all the previously subscribed channels
    redisSub.unsubscribe();
});

server.listen(app.get('port'), function() {
    log.info('Server running on port ' + app.get('port'));
});

//
// Connect to redis  server
//
//For localhost password at /etc/redis/redis.conf at requirepass
var redis = require('redis');
//redis.debug_mode = true;

var url = require('url');

if (typeof process.env.REDISLOCAL_URL !== "undefined") {
    var redisURL = url.parse(process.env.REDISLOCAL_URL);
} else {
    var redisURL = url.parse(process.env.REDISCLOUD_URL);
}

var redisPub = redis.createClient(redisURL.port, redisURL.hostname, {
    no_ready_check: true
});
redisPub.auth(redisURL.auth.split(":")[1], function() {
    log.debug("Redis Pub authenticated");
});
redisPub.on("ready", function(err) {
    log.debug("Redis Pub ready on " + redisURL.hostname);
});
redisPub.on("error", function(err) {
    log.error("Redis Error " + err);
});
redisPub.on("end", function() {
    log.error("Redis Pub connection closed ");
});

var redisSub = redis.createClient(redisURL.port, redisURL.hostname, {
    no_ready_check: true
});
redisSub.auth(redisURL.auth.split(":")[1], function() {
    log.debug("Redis Sub authenticated");
});
redisSub.on("ready", function() {
    log.debug("Redis Sub ready on " + redisURL.hostname);
});

redisSub.on("error", function(err) {
    log.error("Redis Error " + err);
});

redisSub.on("subscribe", function(channel, count) {
    log.debug("Client subscribed to " + channel + ", " + count + " total subscriptions");
});

redisSub.on("unsubscribe", function(channel, count) {
    log.debug("Client unsubscribed from " + channel + ", " + count + " total subscriptions");
});

redisSub.on("message", function(channel, message) {
    log.trace("Client channel " + channel + ": " + message);
    primus.write({
        room: channel,
        msg: message
    });
});

redisSub.on("end", function() {
    log.error("Redis Sub connection closed ");
});

// A time channel for debugging
setInterval(function() {
    redisPub.publish('time', new Date().toISOString(), function() {});
}, 5000);
这是package.json

{
  "name": "rtnetsrvredis",
  "version": "0.0.1",
  "description": "Connect a Redis PubSub channel to a primus room",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "CK",
  "license": "MIT",
  "dependencies": {
    "bunyan": "^0.22.3",
    "express": "^4.1.0",
    "hiredis": "^0.1.16",
    "primus": "^2.2.0",
    "redis": "^0.10.1",
    "sync": "^0.2.2",
    "ws": "^0.4.31"
  }
}

日志中是否有相关信息?我的Node.js fu是低于标准杆,但有可能通过重新加载你没有关闭连接,从而达到了计划的限制(我假设你正在使用我们的[雷迪斯云]免费计划进行测试)?你是对的。我使用免费计划,但问题不在于开放连接的数量。我注意到heroku在销毁dyno时(例如,在推送之后)并没有关闭打开的连接,我在代码中添加了第一部分来实现这一点。日志文件显示了事件的顺序,但您必须结合代码阅读。明白-我非常感谢您的帮助,以便我能够更彻底地调查此事-您能给我发一封电子邮件(itamar位于redislabs.com)或一条推特(@itamarhaber),以便我们能够了解这一点吗?谢谢您的帮助。我已经在github()上上传了所有内容,我也会给你发电子邮件。