Node.js real redis和Heroku redis插件之间的差异
在Heroku上运行代码末尾显示的代码时,我发现程序行为的主要差异取决于我使用的是redis服务器还是Heroku redis插件(RedisCloud和RedisToGo均未成功尝试) 测试代码是通过primus rooms从Redis PubSub到websockets的发射器 首先,我将展示预期的程序行为 我的REDISLOCAL_URL环境变量指向运行redis 2.8.6(与RedisCloud版本相同)的我自己的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","
{"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()上上传了所有内容,我也会给你发电子邮件。