mirror of
https://github.com/Hopiu/postal.js.git
synced 2026-03-18 15:00:30 +00:00
274 lines
5.3 KiB
JavaScript
274 lines
5.3 KiB
JavaScript
/*!
|
|
* socket.io-node
|
|
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
|
* MIT Licensed
|
|
*/
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var crypto = require( 'crypto' )
|
|
, Store = require( '../store' )
|
|
, assert = require( 'assert' );
|
|
|
|
/**
|
|
* Exports the constructor.
|
|
*/
|
|
|
|
exports = module.exports = Redis;
|
|
Redis.Client = Client;
|
|
|
|
/**
|
|
* Redis store.
|
|
* Options:
|
|
* - nodeId (fn) gets an id that uniquely identifies this node
|
|
* - redis (fn) redis constructor, defaults to redis
|
|
* - redisPub (object) options to pass to the pub redis client
|
|
* - redisSub (object) options to pass to the sub redis client
|
|
* - redisClient (object) options to pass to the general redis client
|
|
* - pack (fn) custom packing, defaults to JSON or msgpack if installed
|
|
* - unpack (fn) custom packing, defaults to JSON or msgpack if installed
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
function Redis( opts ) {
|
|
opts = opts || {};
|
|
|
|
// node id to uniquely identify this node
|
|
var nodeId = opts.nodeId || function () {
|
|
// by default, we generate a random id
|
|
return Math.abs( Math.random() * Math.random() * Date.now() | 0 );
|
|
};
|
|
|
|
this.nodeId = nodeId();
|
|
|
|
// packing / unpacking mechanism
|
|
if ( opts.pack ) {
|
|
this.pack = opts.pack;
|
|
this.unpack = opts.unpack;
|
|
} else {
|
|
try {
|
|
var msgpack = require( 'msgpack' );
|
|
this.pack = msgpack.pack;
|
|
this.unpack = msgpack.unpack;
|
|
} catch ( e ) {
|
|
this.pack = JSON.stringify;
|
|
this.unpack = JSON.parse;
|
|
}
|
|
}
|
|
|
|
var redis = opts.redis || require( 'redis' )
|
|
, RedisClient = redis.RedisClient;
|
|
|
|
// initialize a pubsub client and a regular client
|
|
if ( opts.redisPub instanceof RedisClient ) {
|
|
this.pub = opts.redisPub;
|
|
} else {
|
|
opts.redisPub || (opts.redisPub = {});
|
|
this.pub = redis.createClient( opts.redisPub.port, opts.redisPub.host, opts.redisPub );
|
|
}
|
|
if ( opts.redisSub instanceof RedisClient ) {
|
|
this.sub = opts.redisSub;
|
|
} else {
|
|
opts.redisSub || (opts.redisSub = {});
|
|
this.sub = redis.createClient( opts.redisSub.port, opts.redisSub.host, opts.redisSub );
|
|
}
|
|
if ( opts.redisClient instanceof RedisClient ) {
|
|
this.cmd = opts.redisClient;
|
|
} else {
|
|
opts.redisClient || (opts.redisClient = {});
|
|
this.cmd = redis.createClient( opts.redisClient.port, opts.redisClient.host, opts.redisClient );
|
|
}
|
|
|
|
Store.call( this, opts );
|
|
|
|
this.sub.setMaxListeners( 0 );
|
|
this.setMaxListeners( 0 );
|
|
}
|
|
;
|
|
|
|
/**
|
|
* Inherits from Store.
|
|
*/
|
|
|
|
Redis.prototype.__proto__ = Store.prototype;
|
|
|
|
/**
|
|
* Publishes a message.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Redis.prototype.publish = function ( name ) {
|
|
var args = Array.prototype.slice.call( arguments, 1 );
|
|
this.pub.publish( name, this.pack( { nodeId : this.nodeId, args : args } ) );
|
|
this.emit.apply( this, ['publish', name].concat( args ) );
|
|
};
|
|
|
|
/**
|
|
* Subscribes to a channel
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Redis.prototype.subscribe = function ( name, consumer, fn ) {
|
|
this.sub.subscribe( name );
|
|
|
|
if ( consumer || fn ) {
|
|
var self = this;
|
|
|
|
self.sub.on( 'subscribe', function subscribe( ch ) {
|
|
if ( name == ch ) {
|
|
function message( ch, msg ) {
|
|
if ( name == ch ) {
|
|
msg = self.unpack( msg );
|
|
|
|
// we check that the message consumed wasnt emitted by this node
|
|
if ( self.nodeId != msg.nodeId ) {
|
|
consumer.apply( null, msg.args );
|
|
}
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
self.sub.on( 'message', message );
|
|
|
|
self.on( 'unsubscribe', function unsubscribe( ch ) {
|
|
if ( name == ch ) {
|
|
self.sub.removeListener( 'message', message );
|
|
self.removeListener( 'unsubscribe', unsubscribe );
|
|
}
|
|
} );
|
|
|
|
self.sub.removeListener( 'subscribe', subscribe );
|
|
|
|
fn && fn();
|
|
}
|
|
} );
|
|
}
|
|
|
|
this.emit( 'subscribe', name, consumer, fn );
|
|
};
|
|
|
|
/**
|
|
* Unsubscribes
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Redis.prototype.unsubscribe = function ( name, fn ) {
|
|
this.sub.unsubscribe( name );
|
|
|
|
if ( fn ) {
|
|
var client = this.sub;
|
|
|
|
client.on( 'unsubscribe', function unsubscribe( ch ) {
|
|
if ( name == ch ) {
|
|
fn();
|
|
client.removeListener( 'unsubscribe', unsubscribe );
|
|
}
|
|
} );
|
|
}
|
|
|
|
this.emit( 'unsubscribe', name, fn );
|
|
};
|
|
|
|
/**
|
|
* Destroys the store
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Redis.prototype.destroy = function () {
|
|
Store.prototype.destroy.call( this );
|
|
|
|
this.pub.end();
|
|
this.sub.end();
|
|
this.cmd.end();
|
|
};
|
|
|
|
/**
|
|
* Client constructor
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
function Client( store, id ) {
|
|
Store.Client.call( this, store, id );
|
|
}
|
|
;
|
|
|
|
/**
|
|
* Inherits from Store.Client
|
|
*/
|
|
|
|
Client.prototype.__proto__ = Store.Client;
|
|
|
|
/**
|
|
* Redis hash get
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.get = function ( key, fn ) {
|
|
this.store.cmd.hget( this.id, key, fn );
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Redis hash set
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.set = function ( key, value, fn ) {
|
|
this.store.cmd.hset( this.id, key, value, fn );
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Redis hash del
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.del = function ( key, fn ) {
|
|
this.store.cmd.hdel( this.id, key, fn );
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Redis hash has
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.has = function ( key, fn ) {
|
|
this.store.cmd.hexists( this.id, key, function ( err, has ) {
|
|
if ( err ) {
|
|
return fn( err );
|
|
}
|
|
fn( null, !!has );
|
|
} );
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Destroys client
|
|
*
|
|
* @param {Number} number of seconds to expire data
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.destroy = function ( expiration ) {
|
|
if ( 'number' != typeof expiration ) {
|
|
this.store.cmd.del( this.id );
|
|
} else {
|
|
this.store.cmd.expire( this.id, expiration );
|
|
}
|
|
|
|
return this;
|
|
};
|