-
Notifications
You must be signed in to change notification settings - Fork 2.9k
pomelo API
Application prototype.
type: method
Get application base path
// cwd: /home/game/ pomelo start // app.getBase() -> /home/game
Application.getBase = function() {
return this.get(Constants.Reserved.BASE);
};
type: method
- filter - provide before and after filter method.
add a filter to before and after filter
Application.filter = function (filter) {
this.before(filter);
this.after(filter);
};
type: method
- bf - before fileter, bf(msg, session, next)
Add before filter.
Application.before = function (bf) {
addFilter(this, Constants.KeyWords.BEFORE_FILTER, bf);
};
type: method
- af - after filter, `af(err, msg, session, resp, next)`
Add after filter.
Application.after = function (af) {
addFilter(this, Constants.KeyWords.AFTER_FILTER, af);
};
type: method
- filter - provide before and after filter method.
add a global filter to before and after global filter
Application.globalFilter = function (filter) {
this.globalBefore(filter);
this.globalAfter(filter);
};
type: method
- bf - before fileter, bf(msg, session, next)
Add global before filter.
Application.globalBefore = function (bf) { addFilter(this, Constants.KeyWords.GLOBAL_BEFORE_FILTER, bf);};
type: method
- af - after filter, `af(err, msg, session, resp, next)`
Add global after filter.
Application.globalAfter = function (af) { addFilter(this, Constants.KeyWords.GLOBAL_AFTER_FILTER, af);};
type: method
- bf - before fileter, bf(serverId, msg, opts, next)
Add rpc before filter.
Application.rpcBefore = function(bf) { addFilter(this, Constants.KeyWords.RPC_BEFORE_FILTER, bf);};
type: method
- af - after filter, `af(serverId, msg, opts, next)`
Add rpc after filter.
Application.rpcAfter = function(af) { addFilter(this, Constants.KeyWords.RPC_AFTER_FILTER, af);};
type: method
- filter - provide before and after filter method.
add a rpc filter to before and after rpc filter
Application.rpcFilter = function(filter) { this.rpcBefore(filter); this.rpcAfter(filter);};
type: method
- name - (optional) name of the component
- component - component instance or factory function of the component
- opts - (optional) construct parameters for the factory function
Load component
Application.load = function(name, component, opts) { if(typeof name !== 'string') { opts = component; component = name; name = null; if(typeof component.name === 'string') { name = component.name; } } if(typeof component === 'function') { component = component(this, opts); } if(!name && typeof component.name === 'string') { name = component.name; } if(name && this.components[name]) { // ignore duplicat component logger.warn('ignore duplicate component: %j', name); return; } this.loaded.push(component); if(name) { // components with a name would get by name throught app.components later. this.components[name] = component; } return this;};
type: method
- key - environment key
- val - environment value
Load Configure json file to settings.
Application.loadConfig = function (key, val) { var env = this.get(Constants.Reserved.ENV); val = require(val); if (val[env]) { val = val[env]; } this.set(key, val); };
type: method
- serverType - server type string
- routeFunc - route function. routeFunc(session, msg, app, cb)
Set the route function for the specified server type.
Examples:
app.route('area', routeFunc);
var routeFunc = function(session, msg, app, cb) { // all request to area would be route to the first area server var areas = app.getServersByType('area'); cb(null, areas[0].id); };
Application.route = function(serverType, routeFunc) { var routes = this.get(Constants.KeyWords.ROUTE); if(!routes) { routes = {}; this.set(Constants.KeyWords.ROUTE, routes); } routes[serverType] = routeFunc; return this;};
type: method
- fun - before close function
Set before stop function. It would perform before servers stop.
Application.beforeStopHook = function(fun) { logger.warn('this method was deprecated in pomelo 0.8'); if(!!fun && typeof fun === 'function') { this.set(Constants.KeyWords.BEFORE_STOP_HOOK, fun); }};
type: method
- cb - callback function
Start application. It would load the default components and start all the loaded components.
Application.start = function(cb) { this.startTime = Date.now(); if(this.state > STATE_INITED) { utils.invokeCallback(cb, new Error('application has already start.')); return; } appUtil.loadDefaultComponents(this); var self = this; var startUp = function() { appUtil.optComponents(self.loaded, Constants.Reserved.START, function(err) { self.state = STATE_START; if(err) { utils.invokeCallback(cb, err); } else { logger.info('%j enter after start...', self.getServerId()); self.afterStart(cb); } }); }; var beforeFun = this.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP]; if(!!beforeFun) { beforeFun.call(null, this, startUp); } else { startUp(); }};
type: method
- setting - the setting of application
- val - the setting's value
- attach - whether attach the settings to application
Assign setting
to val
, or return setting
's value.
Example:
app.set('key1', 'value1'); app.get('key1'); // 'value1' app.key1; // undefined
app.set('key2', 'value2', true); app.get('key2'); // 'value2' app.key2; // 'value2'
Application.set = function (setting, val, attach) { if (arguments.length === 1) { return this.settings[setting]; } this.settings[setting] = val; if(attach) { this[setting] = val; } return this;};
type: method
- setting - application setting
Get property from setting
Application.get = function (setting) { return this.settings[setting];};
type: method
- setting - application setting
Check if setting
is enabled.
Application.enabled = function (setting) { return !!this.get(setting);};
type: method
- setting - application setting
Check if setting
is disabled.
Application.disabled = function (setting) { return !this.get(setting);};
type: method
- setting - application setting
Enable setting
.
Application.enable = function (setting) { return this.set(setting, true);};
type: method
- setting - application setting
Disable setting
.
Application.disable = function (setting) { return this.set(setting, false);};
type: method
- env - application environment
- fn - callback function
- type - server type
Configure callback for the specified env and server type. When no env is specified that callback will be invoked for all environments and when no type is specified that callback will be invoked for all server types.
Examples:
app.configure(function(){ // executed for all envs and server types });
app.configure('development', function(){ // executed development env });
app.configure('development', 'connector', function(){ // executed for development env and connector server type });
Application.configure = function (env, type, fn) { var args = [].slice.call(arguments); fn = args.pop(); env = type = Constants.Reserved.ALL; if(args.length > 0) { env = args[0]; } if(args.length > 1) { type = args[1]; } if (env === Constants.Reserved.ALL || contains(this.settings.env, env)) { if (type === Constants.Reserved.ALL || contains(this.settings.serverType, type)) { fn.call(this); } } return this;};
type: method
- module - (optional) module id or provoided by module.moduleId
- module - module object or factory function for module
- opts - construct parameter for module
Register admin modules. Admin modules is the extends point of the monitor system.
Application.registerAdmin = function(moduleId, module, opts) { var modules = this.get(Constants.KeyWords.MODULE); if(!modules) { modules = {}; this.set(Constants.KeyWords.MODULE, modules); } if(typeof moduleId !== 'string') { opts = module; module = moduleId; if(module) { moduleId = module.moduleId; } } if(!moduleId){ return; } modules[moduleId] = { moduleId: moduleId, module: module, opts: opts };};
type: method
- plugin - plugin instance
- opts - (optional) construct parameters for the factory function
Use plugin.
Application.use = function(plugin, opts) { if(!plugin.components) { logger.error('invalid components, no components exist'); return; } var self = this; var dir = path.dirname(plugin.components); if(!fs.existsSync(plugin.components)) { logger.error('fail to find components, find path: %s', plugin.components); return; } fs.readdirSync(plugin.components).forEach(function (filename) { if (!/\.js$/.test(filename)) { return; } var name = path.basename(filename, '.js'); var param = opts[name] || {}; var absolutePath = path.join(dir, Constants.Dir.COMPONENT, filename); if(!fs.existsSync(absolutePath)) { logger.error('component %s not exist at %s', name, absolutePath); } else { self.load(require(absolutePath), param); } }); // load events if(!plugin.events) { return; } else { if(!fs.existsSync(plugin.events)) { logger.error('fail to find events, find path: %s', plugin.events); return; } fs.readdirSync(plugin.events).forEach(function (filename) { if (!/\.js$/.test(filename)) { return; } var absolutePath = path.join(dir, Constants.Dir.EVENT, filename); if(!fs.existsSync(absolutePath)) { logger.error('events %s not exist at %s', filename, absolutePath); } else { bindEvents(require(absolutePath), self); } }); }};
type: method
- name - transaction name
- conditions - functions which are called before transaction
- handlers - functions which are called during transaction
- retry - retry times to execute handlers if conditions are successfully executed
Application transaction. Transcation includes conditions and handlers, if conditions are satisfied, handlers would be executed. And you can set retry times to execute handlers. The transaction log is in file logs/transaction.log.
Application.transaction = function(name, conditions, handlers, retry) { appManager.transaction(name, conditions, handlers, retry);};
type: method
Get master server info.
Application.getMaster = function() { return this.master;};
type: method
Get current server info.
Application.getCurServer = function() { return this.curServer;};
type: method
Get current server id.
Application.getServerId = function() { return this.serverId;};
type: method
Get current server type.
Application.getServerType = function() { return this.serverType;};
type: method
Get all the current server infos.
Application.getServers = function() { return this.servers;};
type: method
Get all server infos from servers.json.
Application.getServersFromConfig = function() { return this.get(Constants.KeyWords.SERVER_MAP);};
type: method
Get all the server type.
Application.getServerTypes = function() { return this.serverTypes;};
type: method
- serverId - server id
Get server info by server id from current server cluster.
Application.getServerById = function(serverId) { return this.servers[serverId];};
type: method
- serverId - server id
Get server info by server id from servers.json.
Application.getServerFromConfig = function(serverId) { return this.get(Constants.keyWords.SERVER_MAP)[serverId];};
type: method
- serverType - server type
Get server infos by server type.
Application.getServersByType = function(serverType) { return this.serverTypeMaps[serverType];};
type: method
- server - server info. it would check current server
Check the server whether is a frontend server
Application.isFrontend = function(server) { server = server || this.getCurServer(); return !!server && server.frontend === 'true';};
type: method
- server - server info. it would check current server
Check the server whether is a backend server
Application.isBackend = function(server) { server = server || this.getCurServer(); return !!server && !server.frontend;};
type: method
Check whether current server is a master server
Application.isMaster = function() { return this.serverType === Constants.Reserved.MASTER;};
type: method
- servers - new server info list
Add new server info to current application in runtime.
Application.addServers = function(servers) { if(!servers || !servers.length) { return; } var item, slist; for(var i=0, l=servers.length; i < l; i++) { item = servers[i]; // update global server map this.servers[item.id] = item; // update global server type map slist = this.serverTypeMaps[item.serverType]; if(!slist) { this.serverTypeMaps[item.serverType] = slist = []; } replaceServer(slist, item); // update global server type list if(this.serverTypes.indexOf(item.serverType) < 0) { this.serverTypes.push(item.serverType); } } this.event.emit(events.ADD_SERVERS, servers);};
type: method
- ids - server id list
Remove server info from current application at runtime.
Application.removeServers = function(ids) { if(!ids || !ids.length) { return; } var id, item, slist; for(var i=0, l=ids.length; i < l; i++) { id = ids[i]; item = this.servers[id]; if(!item) { continue; } // clean global server map delete this.servers[id]; // clean global server type map slist = this.serverTypeMaps[item.serverType]; removeServer(slist, id); // TODO: should remove the server type if the slist is empty? } this.event.emit(events.REMOVE_SERVERS, ids);};
type: method
- server - id map
Replace server info from current application at runtime.
Application.replaceServers = function(servers) { if(!servers){ return; } this.servers = servers; this.serverTypeMaps = {}; this.serverTypes = []; var serverArray = []; for(var id in servers){ var server = servers[id]; var serverType = server[Constants.Reserved.SERVER_TYPE]; var slist = this.serverTypeMaps[serverType]; if(!slist) { this.serverTypeMaps[serverType] = slist = []; } this.serverTypeMaps[serverType].push(server); // update global server type list if(this.serverTypes.indexOf(serverType) < 0) { this.serverTypes.push(serverType); } serverArray.push(server); } this.event.emit(events.REPLACE_SERVERS, serverArray);};
type: method
- crons - new crons would be added in application
Add crons from current application at runtime.
Application.addCrons = function(crons) { if(!crons || !crons.length) { logger.warn('crons is not defined.'); return; } this.event.emit(events.ADD_CRONS, crons);};
type: method
- crons - old crons would be removed in application
Remove crons from current application at runtime.
Application.removeCrons = function(crons) { if(!crons || !crons.length) { logger.warn('ids is not defined.'); return; } this.event.emit(events.REMOVE_CRONS, crons);};var replaceServer = function(slist, serverInfo) { for(var i=0, l=slist.length; i < l; i++) { if(slist[i].id === serverInfo.id) { slist[i] = serverInfo; return; } } slist.push(serverInfo);};var removeServer = function(slist, id) { if(!slist || !slist.length) { return; } for(var i=0, l=slist.length; i < l; i++) { if(slist[i].id === id) { slist.splice(i, 1); return; } }};var contains = function(str, settings) { if(!settings) { return false; } var ts = settings.split("|"); for(var i=0, l=ts.length; i < l; i++) { if(str === ts[i]) { return true; } } return false;};var bindEvents = function(Event, app) { var emethods = new Event(app); for(var m in emethods) { if(typeof emethods[m] === 'function') { app.event.on(m, emethods[m].bind(emethods)); } }};var addFilter = function(app, type, filter) { var filters = app.get(type); if(!filters) { filters = []; app.set(type, filters); } filters.push(filter);};
Service that maintains backend sessions and the communiation with frontend servers.
BackendSessionService would be created in each server process and maintains backend sessions for current process and communicates with the relative frontend servers.
BackendSessionService instance could be accessed by app.get('backendSessionService')
or app.backendSessionService.
type: method
- frontendId - frontend server id that session attached
- sid - session id
- cb - callback function. args: cb(err, BackendSession)
Get backend session by frontend server id and session id.
BackendSessionService.prototype.get = function(frontendId, sid, cb) { var namespace = 'sys'; var service = 'sessionRemote'; var method = 'getBackendSessionBySid'; var args = [sid]; rpcInvoke(this.app, frontendId, namespace, service, method, args, BackendSessionCB.bind(null, this, cb));};
type: method
- frontendId - frontend server id that session attached
- uid - user id binded with the session
- cb - callback function. args: cb(err, BackendSessions)
Get backend sessions by frontend server id and user id.
BackendSessionService.prototype.getByUid = function(frontendId, uid, cb) { var namespace = 'sys'; var service = 'sessionRemote'; var method = 'getBackendSessionsByUid'; var args = [uid]; rpcInvoke(this.app, frontendId, namespace, service, method, args, BackendSessionCB.bind(null, this, cb));};
type: method
- frontendId - cooperating frontend server id
- sid - session id
- cb - callback function
Kick a session by session id.
BackendSessionService.prototype.kickBySid = function(frontendId, sid, cb) { var namespace = 'sys'; var service = 'sessionRemote'; var method = 'kickBySid'; var args = [sid]; rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);};
type: method
- frontendId - cooperating frontend server id
- uid - user id
- cb - callback function
Kick sessions by user id.
BackendSessionService.prototype.kickByUid = function(frontendId, uid, cb) { var namespace = 'sys'; var service = 'sessionRemote'; var method = 'kickByUid'; var args = [uid]; rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);};
BackendSession is the proxy for the frontend internal session passed to handlers and it helps to keep the key/value pairs for the server locally. Internal session locates in frontend server and should not be accessed directly.
The mainly operation on backend session should be read and any changes happen in backend session is local and would be discarded in next request. You have to push the changes to the frontend manually if necessary. Any push would overwrite the last push of the same key silently and the changes would be saw in next request. And you have to make sure the transaction outside if you would push the session concurrently in different processes.
See the api below for more details.
type: method
- uid - user id
- cb - callback function
Bind current session with the user id. It would push the uid to frontend server and bind uid to the frontend internal session.
BackendSession.prototype.bind = function(uid, cb) { var self = this; this.__sessionService__.bind(this.frontendId, this.id, uid, function(err) { if(!err) { self.uid = uid; } utils.invokeCallback(cb, err); });};
type: method
- uid - user id
- cb - callback function
Unbind current session with the user id. It would push the uid to frontend server and unbind uid from the frontend internal session.
BackendSession.prototype.unbind = function(uid, cb) { var self = this; this.__sessionService__.unbind(this.frontendId, this.id, uid, function(err) { if(!err) { self.uid = null; } utils.invokeCallback(cb, err); });};
type: method
- key - key
- value - value
Set the key/value into backend session.
BackendSession.prototype.set = function(key, value) { this.settings[key] = value;};
type: method
- key - key
Get the value from backend session by key.
BackendSession.prototype.get = function(key) { return this.settings[key];};
type: method
- key - key
- cb - callback function
Push the key/value in backend session to the front internal session.
BackendSession.prototype.push = function(key, cb) { this.__sessionService__.push(this.frontendId, this.id, key, this.get(key), cb);};
type: method
- cb - callback function
Push all the key/values in backend session to the frontend internal session.
BackendSession.prototype.pushAll = function(cb) { this.__sessionService__.pushAll(this.frontendId, this.id, this.settings, cb);};
Create and maintain channels for server local.
ChannelService is created by channel component which is a default loaded component of pomelo and channel service would be accessed by app.get('channelService')
.
type: method
- name - channel's name
Create channel with name.
ChannelService.prototype.createChannel = function(name) { if(this.channels[name]) { return this.channels[name]; } var c = new Channel(name, this); this.channels[name] = c; return c;};
type: method
- name - channel's name
- create - if true, create channel
Get channel by name.
ChannelService.prototype.getChannel = function(name, create) { var channel = this.channels[name]; if(!channel && !!create) { channel = this.channels[name] = new Channel(name, this); } return channel;};
type: method
- name - channel name
Destroy channel by name.
ChannelService.prototype.destroyChannel = function(name) { delete this.channels[name];};
type: method
- route - message route
- msg - message that would be sent to client
- uids - the receiver info list, [{uid: userId, sid: frontendServerId}]
- opts - user-defined push options, optional
- cb - cb(err)
Push message by uids. Group the uids by group. ignore any uid if sid not specified.
ChannelService.prototype.pushMessageByUids = function(route, msg, uids, opts, cb) { if(typeof route !== 'string') { cb = opts; opts = uids; uids = msg; msg = route; route = msg.route; } if(!cb && typeof opts === 'function') { cb = opts; opts = {}; } if(!uids || uids.length === 0) { utils.invokeCallback(cb, new Error('uids should not be empty')); return; } var groups = {}, record; for(var i=0, l=uids.length; i < l; i++) { record = uids[i]; add(record.uid, record.sid, groups); } sendMessageByGroup(this, route, msg, groups, opts, cb);};
type: method
- stype - frontend server type string
- route - route string
- msg - message
- opts - user-defined broadcast options, optional
- cb - callback
Broadcast message to all the connected clients.
ChannelService.prototype.broadcast = function(stype, route, msg, opts, cb) { var app = this.app; var namespace = 'sys'; var service = 'channelRemote'; var method = 'broadcast'; var servers = app.getServersByType(stype); if(!servers || servers.length === 0) { // server list is empty utils.invokeCallback(cb); return; } var count = servers.length; var successFlag = false; var latch = countDownLatch.createCountDownLatch(count, function() { if(!successFlag) { utils.invokeCallback(cb, new Error('broadcast fails')); return; } utils.invokeCallback(cb, null); }); var genCB = function() { return function(err) { if(err) { logger.error('[broadcast] fail to push message, err:' + err.stack); latch.done(); return; } successFlag = true; latch.done(); }; }; opts = {type: 'broadcast', userOptions: opts || {}}; // for compatiblity opts.isBroadcast = true; if(opts.userOptions) { opts.binded = opts.userOptions.binded; opts.filterParam = opts.userOptions.filterParam; } for(var i=0, l=count; i < l; i++) { app.rpcInvoke(servers[i].id, {namespace: namespace, service: service, method: method, args: [route, msg, opts]}, genCB()); }};
Channel maintains the receiver collection for a subject. You can add users into a channel and then broadcast message to them by channel.
type: method
- uid - user id
- sid - frontend server id which user has connected to
Add user to channel.
Channel.prototype.add = function(uid, sid) { if(this.state > ST_INITED) { return false; } else { var res = add(uid, sid, this.groups); if(res) { this.records[uid] = {sid: sid, uid: uid}; } return res; }};
type: method
- uid - user id
- sid - frontend server id which user has connected to.
Remove user from channel.
Channel.prototype.leave = function(uid, sid) { if(!uid || !sid) { return false; } delete this.records[uid]; var res = deleteFrom(uid, sid, this.groups[sid]); if(this.groups[sid] && this.groups[sid].length === 0) { delete this.groups[sid]; } return res;};
type: method
Get channel members.
Notice: Heavy operation.
Channel.prototype.getMembers = function() { var res = [], groups = this.groups; var group, i, l; for(var sid in groups) { group = groups[sid]; for(i=0, l=group.length; i < l; i++) { res.push(group[i]); } } return res;};
type: method
- uid - user id
Get Member info.
Channel.prototype.getMember = function(uid) { return this.records[uid];};
type: method
Destroy channel.
Channel.prototype.destroy = function() { this.state = ST_DESTROYED; this.__channelService__.destroyChannel(this.name);};
type: method
- route - message route
- msg - message that would be sent to client
- opts - user-defined push options, optional
- cb - callback function
Push message to all the members in the channel
Channel.prototype.pushMessage = function(route, msg, opts, cb) { if(this.state !== ST_INITED) { utils.invokeCallback(new Error('channel is not running now')); return; } if(typeof route !== 'string') { cb = opts; opts = msg; msg = route; route = msg.route; } if(!cb && typeof opts === 'function') { cb = opts; opts = {}; } sendMessageByGroup(this.__channelService__, route, msg, this.groups, opts, cb);};
Session service maintains the internal session for each client connection.
Session service is created by session component and is only available in frontend servers. You can access the service by app.get('sessionService')
or app.sessionService
in frontend servers.
type: method
- uid - user id asscociated with the session
- cb - callback function
Kick all the session offline under the user id.
SessionService.prototype.kick = function(uid, reason, cb) { // compatible for old kick(uid, cb); if(typeof reason === 'function') { cb = reason; reason = 'kick'; } var sessions = this.getByUid(uid); if(sessions) { // notify client for(var i=0, l=sessions.length; i < l; i++) { sessions[i].closed(reason); } process.nextTick(function() { utils.invokeCallback(cb); }); } else { process.nextTick(function() { utils.invokeCallback(cb); }); }};
type: method
- sid - session id
- cb - callback function
Kick a user offline by session id.
SessionService.prototype.kickBySessionId = function(sid, cb) { var session = this.get(sid); if(session) { // notify client session.closed('kick'); process.nextTick(function() { utils.invokeCallback(cb); }); } else { process.nextTick(function() { utils.invokeCallback(cb); }); }};
Expose createApplication()
.
type: method
Create an pomelo application.
Pomelo.createApp = function (opts) { var app = application; app.init(opts); self.app = app; return app;};
-
- getBase
- filter
- before
- after
- globalFilter
- globalBefore
- globalAfter
- rpcBefore
- rpcAfter
- rpcFilter
- load
- loadConfig
- route
- beforeStopHook
- start
- set
- get
- enabled
- disabled
- enable
- disable
- configure
- registerAdmin
- use
- transaction
- getMaster
- getCurServer
- getServerId
- getServerType
- getServers
- getServersFromConfig
- getServerTypes
- getServerById
- getServerFromConfig
- getServersByType
- isFrontend
- isBackend
- isMaster
- addServers
- removeServers
- replaceServers
- addCrons
- removeCrons
-
- get
- getByUid
- kickBySid
- kickByUid
-
- bind
- unbind
- set
- get
- push
- pushAll
-
- createChannel
- getChannel
- destroyChannel
- pushMessageByUids
- broadcast
-
- add
- leave
- getMembers
- getMember
- destroy
- pushMessage
-
- kick
- kickBySessionId
-
- createApp