Source: sharedmemory.js

/**
* @project SharedMemory
* @author Dx.Yang <x6doooo@gmail.com>
* @version 0.0.1 
* @license See LICENSE-MIT file included in this distribution.
*/

/**
* Module dependencies.
*/
var cluster = require('cluster');

var errDesc = {
    '1': '[ERROR] sharedMemory.init(config) => config.manager is wrong type!'
};

/**
* @class Manager
* @classdesc 直接控制共享内存的类
* @constructor
* @return A new instance of Manager
*/
var Manager = function() {

    var self = this;
    
    // 初始化共享内存
    self.__sharedMemory__ = {};

    if(cluster.isMaster) {
        
        // manager是clusterMaster时,监听并处理来自worker的请求
        cluster.on('online', function(worker) {
            worker.on('message', function(data) {
                if (!data.isSharedMemoryMessage) return;
                self.handle(data);
                return false;
            });
        });
    
    }else{
    
        // manager是clusterWorker时,监听并处理来自clusterMaster转发的请求
        process.on('message', function(data) {
            if (!data.isSharedMemoryMessage) return;
            self.handle(data);
        });

    }
};

/**
* @function handle
* @private
* @instance
* @memberOf Manager
* @param {object} data
*/
Manager.prototype.handle = function(data) {
    var self = this;
    var value = this[data.method](data);

    var msg = {
        isSharedMemoryMessage: true,
        id: data.id,
        uuid: data.uuid,
        value: value
    };

    if(cluster.isMaster) {
        cluster.workers[data.id].send(msg);
        return;
    }

    process.send(msg);

};

/**
* @function set
* @private
* @instance
* @memberOf Manager
* @param {object} data
* @returns {string} 'OK'
*/
Manager.prototype.set = function(data) {
    this.__sharedMemory__[data.key] = data.value;
    return 'OK';
};

/**
* @function get
* @private
* @instance
* @memberOf Manager
* @param {object} data
* @returns {*}
*/
Manager.prototype.get = function(data) {
    return this.__sharedMemory__[data.key];
};

/**
* @class User
* @classdesc 通过IPC读写共享内存的类
* @constructor
* @returns A new instance of User
*/
var User = function() {

    var self = this;
    self.__uuid__ = 0;

    /** 
    * 缓存读写操作的回调函数
    * @member {Object} __getCallbacks__
    * @private
    * @instance
    * @memberOf User
    */
    self.__getCallbacks__ = {};

    // 监听读写之后的回信
    process.on('message', function(data) {

        // sharememory的通信标记
        if (!data.isSharedMemoryMessage) return;
        var cb = self.__getCallbacks__[data.uuid];
        if (cb && typeof cb == 'function') {
            cb(data.value)
        }
        self.__getCallbacks__[data.uuid] = undefined;

    });

};

/**
* 获取每次通信的uuid
* @method uuid
* @private
* @instance
* @memberOf User
* @returns {number}
*/
User.prototype.uuid = function() {
    var i = this.__uuid__;
    return this.__uuid__ = i = i > 65535 ? 0 : i + 1;
};

/**
* 写入数据
* @method set
* @instance
* @memberOf User
* @param {string} key - 键
* @param {*} value - 值(不建议使用function类型)
* @param {User~setCallback} cb - 回调函数
*/
/**
* @callback User~setCallback
* @param {string} data - 'OK'表示成功
*/
User.prototype.set = function(key, value, cb) {
    this.handle('set', key, value, cb);
};

/**
* 读取数据
* @method get
* @instance
* @memberOf User
* @param {string} key - 键
* @param {User~getCallback} cb - 回调函数
*/
/**
* @callback User~getCallback
* @param {*} data - key对应的数据
*/
User.prototype.get = function(key, cb) {
    this.handle('get', key, null, cb);
};

/**
* 删除数据
* @method remove
* @instance
* @memberOf User
* @param {string} key - 键
* @param {User~removeCallback} cb - 回调函数
*/
/**
* @callback User~removeCallback
* @param {string} data - 'OK'表示成功
*/
User.prototype.remove = function(key, cb) {
    this.set(key, undefined, cb);
};

/**
* 处理通信的方法
* @method handle
* @instance
* @private
* @memberOf User
* @param {string} [method=set|get]
* @param {string} key
* @param {*} value
* @param {User~handleCallback} cb - 回调函数
*/
/**
* @callback User~handleCallback
* @param {string} data
*/
User.prototype.handle = function(method, key, value, cb) {

    var self = this;
    var uuid = self.uuid();

    process.send({
        isSharedMemoryMessage: true,
        method: method,
        id: cluster.worker.id,
        uuid: uuid,
        key: key,
        value: value
    });

    self.__getCallbacks__[uuid] = cb;

};

/**
* @class Transfer
* @classdesc 如果Manager是worker进城,就需要Master进城扮演通信的中转站
* @constructor
* @param {number} whoIsManager - Manager所在worker进程的id
* @return A new instance of Transfer
*/
var Transfer = function(whoIsManager) {
    cluster.on('online', function(worker) {
        worker.on('message', function(data) {
            if (!data.isSharedMemoryMessage) return;
            if (worker.id === whoIsManager) {
                // 转发给user
                cluster.workers[data.id].send(data);
                return;
            }
            // 转发给manager
            cluster.workers[whoIsManager].send(data);
        });
    });
};


/**
* 初始化函数,判断所在进程类型和设置,实例化相应的类
* @method init
* @param {object} [config]
* @param {number} [config.manager] - 如果需要用work进程做共享内存的manager,指定其worker.id即可
* @returns {instance} Manager | User | Transfer 的实例
*/
function init(config) {

    config = config || {};

    var defaultConfig = {
        expire: 30 * 60 * 1000
    };

    // undefined => master | cluster_id => cluster
    var whoIsManager = config.manager || defaultConfig.manager;

    /*
        Manager(cluster worker) <--> Transfer(cluster master) <--> User、User、User...(cluster worker)
    */
    if (whoIsManager) {

        if(typeof whoIsManager !== 'number') {
            throw new Error(errDesc[1]);
            return;
        }
        
        if (cluster.isMaster) {
            return new Transfer(whoIsManager);
        }
        
        if(cluster.worker.id === whoIsManager) {
            return new Manager;
        }
        
        return new User;

    } else {

        /*
            Manager(cluster master) <--> User(cluster worker)
        */        

        if (cluster.isMaster) {
            return new Manager();
        }

        return new User;

    }

}

exports.Manager  = Manager;
exports.User     = User;
exports.Transfer = Transfer;
exports.init     = init;