Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

v1.0.0

  • Loading branch information...
commit 0da0c5ae627b84dc29df4dca8f46540d7b6ca698 1 parent c541185
@dead-horse dead-horse authored
View
9 Makefile
@@ -0,0 +1,9 @@
+TEST = test/*.js
+TESTTIMEOUT = 5000
+REPORTER = spec
+
+test:
+ @NODE_ENV=test ./node_modules/mocha/bin/mocha \
+ --reporter $(REPORTER) --timeout $(TESTTIMEOUT) $(TEST)
+
+.PHONY: test
View
1  index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/multi_redis');
View
215 lib/multi_redis.js
@@ -7,9 +7,9 @@
/**
* Module dependencies.
*/
-
var redis = require('redis');
-
+var EventEmitter = require('events').EventEmitter;
+var util = require('util');
/**
* Initialize MutiRedis with the given `options`.
*
@@ -17,64 +17,191 @@ var redis = require('redis');
* @api public
*/
function MultiRedis(options) {
+ EventEmitter.call(this);
options = options || {};
+ options.host = options.host || ['127.0.0.1'];
+ options.port = options.port || [6379];
if (!(options.host instanceof Array)) {
options.host = [options.host];
options.port = options.port && [options.port];
options.socket = options.socket && [options.socket];
- new redis.createClient(options.port || options.socket, options.host, options);
}
+ this.debug = options.debug;
this.clients = [];
- this.num = this.clients.length;
- this.infos = [];
- console.log(options.host.length);
+ this.index = 0;
+ this.speedFirst = options.speedFirst;
+ this.pingInterval = options.pingInterval || 3000;
+ if(this.speedFirst) {
+ this._ping();
+ }
+
+
for (var i=0, len=options.host.length; i!=len; ++i) {
var client = new redis.createClient(options.port[i] || options.socket[i], options.host[i], options);
+ if(options.debug) {
+ console.log("%s: Redis connection to %s:%d",Date(), client.host, client.port);
+ }
+ this.clients.push(client);
var self = this;
- client.on('connect', function() {
- console.log('connect');
- self.clients.push(client);
- ++this.num;
- });
- client.on('end', function() {
- for(var i=0, len=self.clients.length; i!=len; ++i) {
- if(self.clients[i] === client) {
- console.log('end');
- self.clients.splice(0, 1);
- --this.num;
+ (function(client) {
+ client.on('end', function() {
+ for(var i=0, len=self.clients.length; i!=len; ++i) {
+ if(self.clients[i] === client) {
+ self.clients.splice(i, 1);
+ if(options.debug) {
+ console.log("%s: Redis disconnect to %s:%d", Date(), client.host, client.port);
+ }
+ if(self.clients.length===0) {
+ self.emit('error', new Error('All servers are down.'));
+ }
+ break;
+ }
}
- }
- })
- client.on('error', function(err) {
- console.log(err);
- })
+ self.emit('end', client);
+ })
+ client.on('error', function(err) {
+ if (options.debug && (err.message&&err.message.indexOf('connect ECONNREFUSED') < 0)) {
+ console.log(err.message);
+ } else {
+ self.emit('error', err);
+ }
+ })
+ client.on("connect", function() {
+ for(var i=0, len=self.clients.length; i!=len; ++i) {
+ if(self.clients[i]===client) return;
+ }
+ self.clients.push(client);
+ if(options.debug) {
+ console.log("%s: Redis connection to %s:%d",Date(), client.host, client.port);
+ }
+ self.emit('connect', client);
+ });
+ }) (client);
}
+}
- // if (options.pass) {
- // for (var i=0, len=this.num; i!=len; ++i) {
- // this.clients[i].auth(options.pass, function(err) {
- // if (err) throw err;
- // });
- // }
- // }
+util.inherits(MultiRedis, EventEmitter);
+/**
+ * redis get like commands
+ * @type {Array}
+ */
+var getCmds = ['get', 'mget', 'exists', 'getbit', 'hget', 'hmget'];
+/**
+ * redis set like commands
+ * @type {Array}
+ */
+var setCmds = ['set', 'setnx', 'setex', 'append', 'del', 'hset', 'hmset', 'auth', 'select'];
- // if(options.db) {
- // var self = this;
- // for (var i=0, len=self.num; i!=len; ++i) {
- // (function(i){
- // var client = self.clients[i];
- // client.select(options.db);
- // client.on("connect", function() {
- // client.send_anyways = true;
- // client.select(options.db);
- // client.send_anyways = false;
- // })
- // })(i);
- // }
- // }
+/**
+ * change arguments to array
+ * @param {[type]} args [description]
+ * @return {[type]}
+ */
+var toArray = function(args) {
+ var arr = [];
+ for (var i=0, len=args.length; i<len; ++i) {
+ arr[i] = args[i];
+ }
+ return arr;
}
-MultiRedis.create = function(options) {
+
+getCmds.forEach(function(command) {
+ MultiRedis.prototype[command] = function(args, callback) {
+ var client = this.clients[this._getIndex()];
+ client[command].apply(client, toArray(arguments));
+ if (this.debug) {
+ console.log("Command %s on redis server %s:%d", command, client.host, client.port);
+ }
+ }
+})
+
+setCmds.forEach(function(command) {
+ MultiRedis.prototype[command] = function(args, callback) {
+ var called = 0;
+ var lastArgType = typeof arguments[arguments.length-1];
+
+ if (this.debug) {
+ console.log("Command %s on multi redis servers", command);
+ }
+ var arrArg = toArray(arguments);
+ if (lastArgType==='function') {
+ var fn = arrArg.pop();
+ var self = this;
+ var cb = function(err) {
+ if(err && err instanceof Error) {
+ return fn.apply(null, toArray(arguments));
+ }
+ if (called === self.clients.length-1) {
+ fn.apply(null, toArray(arguments));
+ }
+ ++called;
+ }
+ arrArg.push(cb);
+ }
+ for(var i=0, len=this.clients.length; i!=len; ++i) {
+ this.clients[i][command].apply(this.clients[i], arrArg);
+ }
+ }
+})
+/**
+ * end all the redis server
+ * @api public
+ */
+MultiRedis.prototype.end = function() {
+ for (var i=0, len=this.clients.length; i!=len; ++i) {
+ this.clients[i].end();
+ }
+ this.clients = [];
+ if (this.debug) {
+ console.log('All redis server end');
+ }
+}
+/**
+ * return which server to read
+ * @api private
+ */
+MultiRedis.prototype._getIndex = function() {
+ return this.speedFirst ?
+ this.index : this.index++ % this.clients.length;
+}
+/**
+ * test speed of each server
+ * @return {[type]}
+ */
+MultiRedis.prototype._ping = function() {
+ var self = this;
+ self._pingTimer = setInterval(function() {
+ var min = 100000000;
+ for (var i=0, len=self.clients.length; i!=len; ++i) {
+ (function(i) {
+ var start = new Date().getTime();
+ self.clients[i].ping(function() {
+ var interval = new Date().getTime() - start;
+ if(interval < min) {
+ min = interval;
+ self.index = i;
+ }
+ })
+ })(i)
+ }
+ }, self.pingInterval);
+}
+/**
+ * get an instance of multi redis
+ * or you can use createClient(options), write port and host in the options
+ * @param {array | string} port redis server port
+ * @param {array | string} host redis server host
+ * @param {object} options options
+ * @api public
+ */
+MultiRedis.createClient = function(port, host, options) {
+ if(port instanceof Object) {
+ options = port;
+ } else {
+ options.port = port;
+ options.host = host;
+ }
return new MultiRedis(options);
}
View
17 package.json
@@ -0,0 +1,17 @@
+{
+ "name" : "multi-redis",
+ "description" : "Manage multi redis server",
+ "version" : "1.0.0",
+ "author" : "dead_horse <heyiyu.pt@taobao.com>",
+ "main" : "./index.js",
+ "dependencies" : {"redis" : ">= 0.7.1"},
+ "devDependencies": {
+ "mocha" : ">= 0.9.0",
+ "should" : ">= 0.4.2"
+ },
+ "scripts" : {
+ "test" : "make test"
+ },
+ "keywords" : ["redis", "multi"],
+ "license" : "MIT"
+}
View
47 test.js
@@ -1,6 +1,45 @@
var multiRedis = require('./lib/multi_redis.js');
+var redis = multiRedis.createClient({
+ host : [ "127.0.0.1", "127.0.0.1"],
+ port : [1239, 1240],
+ debug : true,
+ speedFirst : true
+});
-var redis = multiRedis.create({
- host : ["127.0.0.1", "127.0.0.1"],
- port : [1239, 6379]
-});
+
+
+//redis.auth("edp", function(err) {
+//});
+redis.set("abc", 444, function(err, ok){
+ console.log(err, ok);
+ redis.get("abc", function(err, data) {
+ console.log("get first time: %d", data);
+ redis.get("abc", function(err, data){
+ console.log("get second tim: %d", data);
+ redis.del("abc", function(err){
+ redis.get("abc", function(err,data){
+ console.log("get after del: %d", data);
+ redis.setex("abc", 1, 555, function(){
+ redis.get("abc", function(err,data){
+ console.log("get after setex: %d", data);
+ setTimeout(function(){
+ redis.get("abc", function(err, data){
+ console.log("get after 2s: %d", data);
+ })
+ }, 2000);
+ })
+ })
+ })
+ })
+ })
+ })
+})
+// var redis = require('redis');
+// var client = redis.createClient(1239);
+// client.on('connect', function(){
+// console.log('connect')
+// })
+// client.on('error',function(){});
+// client.auth('edp');
+// client.set("123", 123);
+// client.get("123", function(){})
View
3  test/mocha.opts
@@ -0,0 +1,3 @@
+--require node_modules/should
+--require assert
+--growl
View
92 test/multi_redis_test.js
@@ -0,0 +1,92 @@
+var multiRedis = require('../lib/multi_redis');
+
+/**
+ * be sure you have a redis server
+ */
+var client;
+var options = {
+ host : ['127.0.0.1', '127.0.0.1'],
+ port : [1239, 1240],
+ debug : false
+}
+describe('functional test', function() {
+ describe('#createClient()', function(){
+ it('should create redis client ok', function(done) {
+ client = multiRedis.createClient(options);
+ client.clients.should.have.length(2);
+ done();
+ })
+ })
+
+ describe('#set()', function() {
+ it('should set ok', function(done){
+ client.set('test', 'mytest', function(err, ok) {
+ (!err).should.be.ok;
+ console.log(ok);
+ ok.should.equal('OK');
+ done();
+ });
+ })
+ })
+
+ describe('#get()', function() {
+ it('should get ok', function(done) {
+ client.get('test', function(err, data) {
+ (!err).should.be.ok;
+ data.should.equal('mytest');
+ done();
+ })
+ })
+ it('should get ok by other server', function(done) {
+ client.get('test', function(err, data) {
+ (!err).should.be.ok;
+ data.should.equal('mytest');
+ done();
+ })
+ })
+ })
+
+ describe('#del()', function() {
+ it('shoud del ok', function(done) {
+ client.del('test', function(err, data){
+ client.get('test', function(err, data) {
+ (!err).should.be.ok;
+ console.log(err, data);
+ (!data).should.be.ok;
+ client.get('test',function(err, data) {
+ (!err).should.be.ok;
+ (!data).should.be.ok;
+ done();
+ })
+ })
+ })
+ })
+ })
+ describe('#onEvent', function() {
+ it('should emit error', function(done) {
+ client.on('error', function(err) {
+ err.message.should.equal('test error');
+ done();
+ })
+ client.clients[0].emit('error', new Error('test error'));
+ })
+ it('should emit end', function(done) {
+ client.on('end', function(client) {
+ client.port.should.equal(1239);
+ done();
+ })
+ client.clients[0].emit('end', client.clients[0]);
+ })
+ })
+ describe('#end()', function() {
+ it('should emit end', function(done) {
+ var num = options.port.length;
+ setTimeout(function() {
+ client.clients.should.have.length(0);
+ done();
+ }, 200)
+ client.end();
+ })
+ })
+
+})
Please sign in to comment.
Something went wrong with that request. Please try again.