UNPKG

omelop

Version:

[![Build Status](https://travis-ci.org/node-omelop/omelop.svg?branch=master)](https://travis-ci.org/node-omelop/omelop)

889 lines 53.5 kB
"use strict"; /*! * Omelop -- proto * Copyright(c) 2012 xiechengchao <xiecc@163.com> * MIT Licensed */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Application = void 0; /** * Module dependencies. */ const utils = require("./util/utils"); const omelop_logger_1 = require("omelop-logger"); const events_1 = require("events"); const events_2 = require("./util/events"); const appUtil = require("./util/appUtil"); const Constants = require("./util/constants"); const appManager = require("./common/manager/appManager"); const fs = require("fs"); const path = require("path"); const util_1 = require("util"); let logger = (0, omelop_logger_1.getLogger)('omelop', path.basename(__filename)); /** * Application states */ let STATE_INITED = 1; // app has inited let STATE_BEFORE_START = 2; // app before start let STATE_START = 3; // app start let STATE_STARTED = 4; // app has started let STATE_STOPED = 5; // app has stoped class Application { constructor() { this.loaded = []; // loaded component list this.components = {}; // name -> component map this.settings = {}; // collection keep set/get this.event = new events_1.EventEmitter(); // event object to sub/pub events // global server infos this.master = null; // master server info this.servers = {}; // current global server info maps, id -> info this.serverTypeMaps = {}; // current global type maps, type -> [info] this.serverTypes = []; // current global server type list this.usedPlugins = []; // current server custom lifecycle callbacks this.clusterSeq = {}; // cluster id seqence this.astart = utils.promisify(this.start); this.aconfigure = utils.promisify(this.configure); } /** * Initialize the server. * * - setup default configuration */ init(opts) { opts = opts || {}; let base = opts.base || path.dirname(require.main.filename); this.set(Constants.RESERVED.BASE, base); this.base = base; appUtil.defaultConfiguration(this); this.state = STATE_INITED; logger.info('application inited: %j', this.getServerId()); } /** * Get application base path * * // cwd: /home/game/ * omelop start * // app.getBase() -> /home/game * * @return {String} application base path * * @memberOf Application */ getBase() { return this.get(Constants.RESERVED.BASE); } /** * Override require method in application * * @param {String} relative path of file * * @memberOf Application */ require(ph) { return require(path.join(this.getBase(), ph)); } /** * Configure logger with {$base}/config/log4js.json * * @param {Object} logger omelop-logger instance without configuration * * @memberOf Application */ configureLogger(logger) { if (process.env.POMELO_LOGGER !== 'off') { let serverId = this.getServerId(); let base = this.getBase(); let env = this.get(Constants.RESERVED.ENV); let originPath = path.join(base, Constants.FILEPATH.LOG); let presentPath = path.join(base, Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG)); if (this._checkCanRequire(originPath)) { logger.configure(originPath, { serverId: serverId, base: base }); } else if (this._checkCanRequire(presentPath)) { logger.configure(presentPath, { serverId: serverId, base: base }); } else { console.error('logger file path configuration is error.'); } } } /** * add a filter to before and after filter * * @param {Object} filter provide before and after filter method. * A filter should have two methods: before and after. * @memberOf Application */ filter(filter) { this.before(filter); this.after(filter); } /** * Add before filter. * * @param {Object|Function} bf before fileter, bf(msg, session, next) * @memberOf Application */ before(bf) { addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf); } /** * Add after filter. * * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)` * @memberOf Application */ after(af) { addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af); } /** * add a global filter to before and after global filter * * @param {Object} filter provide before and after filter method. * A filter should have two methods: before and after. * @memberOf Application */ globalFilter(filter) { this.globalBefore(filter); this.globalAfter(filter); } /** * Add global before filter. * * @param {Object|Function} bf before fileter, bf(msg, session, next) * @memberOf Application */ globalBefore(bf) { addFilter(this, Constants.KEYWORDS.GLOBAL_BEFORE_FILTER, bf); } /** * Add global after filter. * * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)` * @memberOf Application */ globalAfter(af) { addFilter(this, Constants.KEYWORDS.GLOBAL_AFTER_FILTER, af); } /** * Add rpc before filter. * * @param {Object|Function} bf before fileter, bf(serverId, msg, opts, next) * @memberOf Application */ rpcBefore(bf) { addFilter(this, Constants.KEYWORDS.RPC_BEFORE_FILTER, bf); } /** * Add rpc after filter. * * @param {Object|Function} af after filter, `af(serverId, msg, opts, next)` * @memberOf Application */ rpcAfter(af) { addFilter(this, Constants.KEYWORDS.RPC_AFTER_FILTER, af); } /** * add a rpc filter to before and after rpc filter * * @param {Object} filter provide before and after filter method. * A filter should have two methods: before and after. * @memberOf Application */ rpcFilter(filter) { this.rpcBefore(filter); this.rpcAfter(filter); } load(name, component, opts) { if (typeof name !== 'string') { opts = component; component = name; name = null; } if ((0, util_1.isFunction)(component)) { component = new 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 component; } _checkCanRequire(path) { try { path = require.resolve(path); } catch (err) { return null; } return path; } /** * Load Configure json file to settings.(support different enviroment directory & compatible for old path) * * @param {String} key environment key * @param {String} val environment value * @param {Boolean} reload whether reload after change default false * @return {Server|Mixed} for chaining, or the setting value * @memberOf Application */ loadConfigBaseApp(key, val, reload = false) { let self = this; let env = this.get(Constants.RESERVED.ENV); let originPath = path.join(this.getBase(), val); let presentPath = path.join(this.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val)); let realPath; let tmp; if (self._checkCanRequire(originPath)) { realPath = require.resolve(originPath); let file = require(originPath); if (file[env]) { file = file[env]; } this.set(key, file); } else if (self._checkCanRequire(presentPath)) { realPath = require.resolve(presentPath); let pfile = require(presentPath); this.set(key, pfile); } else { logger.error('invalid configuration with file path: %s', key); } if (!!realPath && !!reload) { const watcher = fs.watch(realPath, function (event, filename) { if (event === 'change') { self.clearRequireCache(require.resolve(realPath)); watcher.close(); self.loadConfigBaseApp(key, val, reload); } }); } } clearRequireCache(path) { const moduleObj = require.cache[path]; if (!moduleObj) { logger.warn('can not find module of truepath', path); return; } if (moduleObj.parent) { // console.log('has parent ',moduleObj.parent); moduleObj.parent.children.splice(moduleObj.parent.children.indexOf(moduleObj), 1); } delete require.cache[path]; } /** * Load Configure json file to settings. * * @param {String} key environment key * @param {String} val environment value * @return {Server|Mixed} for chaining, or the setting value * @memberOf Application */ loadConfig(key, val) { let env = this.get(Constants.RESERVED.ENV); let cfg = require(val); if (cfg[env]) { cfg = cfg[env]; } this.set(key, cfg); } /** * Set the route function for the specified server type. * * Examples: * * app.route('area', routeFunc); * * let routeFunc = function(session, msg, app, cb) { * // all request to area would be route to the first area server * let areas = app.getServersByType('area'); * cb(null, areas[0].id); * }; * * @param {String} serverType server type string * @param {Function} routeFunc route function. routeFunc(session, msg, app, cb) * @return {Object} current application instance for chain invoking * @memberOf Application */ route(serverType, routeFunc) { let routes = this.get(Constants.KEYWORDS.ROUTE); if (!routes) { routes = {}; this.set(Constants.KEYWORDS.ROUTE, routes); } routes[serverType] = routeFunc; return this; } /** * Set before stop function. It would perform before servers stop. * * @param {Function} fun before close function * @return {Void} * @memberOf Application */ beforeStopHook(fun) { logger.warn('this method was deprecated in omelop 0.8'); if (!!fun && typeof fun === 'function') { this.set(Constants.KEYWORDS.BEFORE_STOP_HOOK, fun); } } /** * Start application. It would load the default components and start all the loaded components. * * @param {Function} cb callback function * @memberOf Application */ start(cb) { this.startTime = Date.now(); if (this.state > STATE_INITED) { utils.invokeCallback(cb, new Error('application has already start.')); return; } let self = this; appUtil.startByType(self, function () { appUtil.loadDefaultComponents(self); let startUp = function () { self.state = STATE_BEFORE_START; logger.info('%j enter before start...', self.getServerId()); appUtil.optComponents(self.loaded, Constants.RESERVED.BEFORE_START, function (err) { if (err) { utils.invokeCallback(cb, err); } else { logger.info('%j enter start...', self.getServerId()); 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); } }); } }); }; appUtil.optLifecycles(self.usedPlugins, Constants.LIFECYCLE.BEFORE_STARTUP, self, function (err) { if (err) { utils.invokeCallback(cb, err); } else { startUp(); } }); }); } /** * Lifecycle callback for after start. * * @param {Function} cb callback function * @return {Void} */ afterStart(cb) { if (this.state !== STATE_START) { utils.invokeCallback(cb, new Error('application is not running now.')); return; } let self = this; appUtil.optComponents(this.loaded, Constants.RESERVED.AFTER_START, function (err) { self.state = STATE_STARTED; let id = self.getServerId(); if (!err) { logger.info('%j finish start', id); } appUtil.optLifecycles(self.usedPlugins, Constants.LIFECYCLE.AFTER_STARTUP, self, function (err) { let usedTime = Date.now() - self.startTime; logger.info('%j startup in %s ms', id, usedTime); self.event.emit(events_2.default.START_SERVER, id); cb && cb(err); }); }); } /** * Stop components. * * @param {Boolean} force whether stop the app immediately */ stop(force) { if (this.state > STATE_STARTED) { logger.warn('[omelop application] application is not running now.'); return; } this.state = STATE_STOPED; let self = this; this.stopTimer = setTimeout(function () { process.exit(0); }, Constants.TIME.TIME_WAIT_STOP); let cancelShutDownTimer = function () { if (!!self.stopTimer) { clearTimeout(self.stopTimer); } }; let shutDown = function () { appUtil.stopComps(self.loaded, 0, force, function () { cancelShutDownTimer(); if (force) { process.exit(0); } }); }; let fun = this.get(Constants.KEYWORDS.BEFORE_STOP_HOOK); appUtil.optLifecycles(self.usedPlugins, Constants.LIFECYCLE.BEFORE_SHUTDOWN, self, function (err) { if (err) { console.error(`throw err when beforeShutdown `, err.stack); } else { if (!!fun) { utils.invokeCallback(fun, self, shutDown, cancelShutDownTimer); } else { shutDown(); } } }, cancelShutDownTimer); } set(setting, val, attach) { this.settings[setting] = val; if (attach) { this[setting] = val; } return this; } get(setting) { return this.settings[setting]; } /** * Check if `setting` is enabled. * * @param {String} setting application setting * @return {Boolean} * @memberOf Application */ enabled(setting) { return !!this.get(setting); } /** * Check if `setting` is disabled. * * @param {String} setting application setting * @return {Boolean} * @memberOf Application */ disabled(setting) { return !this.get(setting); } /** * Enable `setting`. * * @param {String} setting application setting * @return {app} for chaining * @memberOf Application */ enable(setting) { return this.set(setting, true); } /** * Disable `setting`. * * @param {String} setting application setting * @return {app} for chaining * @memberOf Application */ disable(setting) { return this.set(setting, false); } configure(env, type, fn) { let 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; } registerAdmin(moduleId, module, opts) { let 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) moduleId = module.constructor.name; } } if (!moduleId) { return; } modules[moduleId] = { moduleId: moduleId, module: module, opts: opts }; } /** * Use plugin. * * @param {Object} plugin plugin instance * @param {[type]} opts (optional) construct parameters for the factory function * @memberOf Application */ use(plugin, opts) { opts = opts || {}; if (!plugin) { throw new Error(`pluin is null!]`); } if (this.usedPlugins.indexOf(plugin) >= 0) { throw new Error(`pluin[${plugin.name} was used already!]`); } if (plugin.components) { for (let componentCtor of plugin.components) { this.load(componentCtor, opts); } } if (plugin.events) { for (let eventCtor of plugin.events) { this.loadEvent(eventCtor, opts); } } this.usedPlugins.push(plugin); console.warn(`used Plugin : ${plugin.name}`); } /** * 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. * * @param {String} name transaction name * @param {Object} conditions functions which are called before transaction * @param {Object} handlers functions which are called during transaction * @param {Number} retry retry times to execute handlers if conditions are successfully executed * @memberOf Application */ transaction(name, conditions, handlers, retry) { appManager.transaction(name, conditions, handlers, retry); } /** * Get master server info. * * @return {Object} master server info, {id, host, port} * @memberOf Application */ getMaster() { return this.master; } /** * Get current server info. * * @return {Object} current server info, {id, serverType, host, port} * @memberOf Application */ getCurServer() { return this.curServer; } /** * Get current server id. * * @return {String|Number} current server id from servers.json * @memberOf Application */ getServerId() { return this.serverId; } /** * Get current server * @returns ServerInfo */ getCurrentServer() { return this.curServer; } /** * Get current server type. * * @return {String|Number} current server type from servers.json * @memberOf Application */ getServerType() { return this.serverType; } /** * Get all the current server infos. * * @return {Object} server info map, key: server id, value: server info * @memberOf Application */ getServers() { return this.servers; } /** * Get all server infos from servers.json. * * @return {Object} server info map, key: server id, value: server info * @memberOf Application */ getServersFromConfig() { return this.get(Constants.KEYWORDS.SERVER_MAP); } /** * Get all the server type. * * @return {Array} server type list * @memberOf Application */ getServerTypes() { return this.serverTypes; } /** * Get server info by server id from current server cluster. * * @param {String} serverId server id * @return {Object} server info or undefined * @memberOf Application */ getServerById(serverId) { return this.servers[serverId]; } /** * Get server info by server id from servers.json. * * @param {String} serverId server id * @return {Object} server info or undefined * @memberOf Application */ getServerFromConfig(serverId) { return this.get(Constants.KEYWORDS.SERVER_MAP)[serverId]; } /** * Get server infos by server type. * * @param {String} serverType server type * @return {Array} server info list * @memberOf Application */ getServersByType(serverType) { return this.serverTypeMaps[serverType]; } /** * Check the server whether is a frontend server * * @param {server} server server info. it would check current server * if server not specified * @return {Boolean} * * @memberOf Application */ isFrontend(server) { server = server || this.getCurServer(); return !!server && server.frontend === 'true'; } /** * Check the server whether is a backend server * * @param {server} server server info. it would check current server * if server not specified * @return {Boolean} * @memberOf Application */ isBackend(server) { server = server || this.getCurServer(); return !!server && !server.frontend; } /** * Check whether current server is a master server * * @return {Boolean} * @memberOf Application */ isMaster() { return this.serverType === Constants.RESERVED.MASTER; } /** * Add new server info to current application in runtime. * * @param {Array} servers new server info list * @memberOf Application */ addServers(servers) { if (!servers || !servers.length) { return; } let item, slist; for (let 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_2.default.ADD_SERVERS, servers); } /** * Remove server info from current application at runtime. * * @param {Array} ids server id list * @memberOf Application */ removeServers(ids) { if (!ids || !ids.length) { return; } let id, item, slist; for (let 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_2.default.REMOVE_SERVERS, ids); } /** * Replace server info from current application at runtime. * * @param {Object} server id map * @memberOf Application */ replaceServers(servers) { if (!servers) { return; } this.servers = servers; this.serverTypeMaps = {}; this.serverTypes = []; let serverArray = []; for (let id in servers) { let server = servers[id]; let serverType = server[Constants.RESERVED.SERVER_TYPE]; let 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_2.default.REPLACE_SERVERS, serverArray); } /** * Add crons from current application at runtime. * * @param {Array} crons new crons would be added in application * @memberOf Application */ addCrons(crons) { if (!crons || !crons.length) { logger.warn('crons is not defined.'); return; } this.event.emit(events_2.default.ADD_CRONS, crons); } /** * Remove crons from current application at runtime. * * @param {Array} crons old crons would be removed in application * @memberOf Application */ removeCrons(crons) { if (!crons || !crons.length) { logger.warn('ids is not defined.'); return; } this.event.emit(events_2.default.REMOVE_CRONS, crons); } /** * 加载一个事件侦听 * @param Event * @param opts */ loadEvent(Event, opts) { let eventInstance = new Event(opts); for (let evt in events_2.AppEvents) { let name = events_2.AppEvents[evt]; let method = eventInstance[name]; if (method) { this.event.on(name, method.bind(eventInstance)); } } } } exports.Application = Application; let replaceServer = function (slist, serverInfo) { for (let i = 0, l = slist.length; i < l; i++) { if (slist[i].id === serverInfo.id) { slist[i] = serverInfo; return; } } slist.push(serverInfo); }; let removeServer = function (slist, id) { if (!slist || !slist.length) { return; } for (let i = 0, l = slist.length; i < l; i++) { if (slist[i].id === id) { slist.splice(i, 1); return; } } }; let contains = function (str, settings) { if (!settings) { return false; } let ts = settings.split('|'); for (let i = 0, l = ts.length; i < l; i++) { if (str === ts[i]) { return true; } } return false; }; let addFilter = function (app, type, filter) { let filters = app.get(type); if (!filters) { filters = []; app.set(type, filters); } filters.push(filter); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwbGljYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvYXBwbGljYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7OztBQUVIOztHQUVHO0FBQ0gsc0NBQXNDO0FBQ3RDLGlEQUFtRDtBQUNuRCxtQ0FBc0M7QUFDdEMsMENBQTZEO0FBQzdELDBDQUEwQztBQUUxQyw4Q0FBOEM7QUFFOUMsMERBQTBEO0FBRTFELHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IsK0JBQWtDO0FBNEJsQyxJQUFJLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztBQWtFNUQ7O0dBRUc7QUFDSCxJQUFJLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBRSxpQkFBaUI7QUFDeEMsSUFBSSxrQkFBa0IsR0FBRyxDQUFDLENBQUMsQ0FBRSxtQkFBbUI7QUFDaEQsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUUsWUFBWTtBQUNsQyxJQUFJLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBRSxrQkFBa0I7QUFDMUMsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDLENBQUUsaUJBQWlCO0FBRXhDLE1BQWEsV0FBVztJQUF4QjtRQUVJLFdBQU0sR0FBaUIsRUFBRSxDQUFDLENBQU8sd0JBQXdCO1FBQ3pELGVBQVUsR0FlTixFQUFFLENBQUMsQ0FBRyx3QkFBd0I7UUFNbEMsYUFBUSxHQUEyQixFQUFFLENBQUMsQ0FBSywwQkFBMEI7UUFDckUsVUFBSyxHQUFHLElBQUkscUJBQVksRUFBRSxDQUFDLENBQUUsaUNBQWlDO1FBUTlELHNCQUFzQjtRQUN0QixXQUFNLEdBQW9CLElBQUksQ0FBQyxDQUFTLHFCQUFxQjtRQUM3RCxZQUFPLEdBQWlDLEVBQUUsQ0FBQyxDQUFVLDhDQUE4QztRQUNuRyxtQkFBYyxHQUFxQyxFQUFFLENBQUMsQ0FBRywyQ0FB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