千裡之行始於足下,一直說想瞭解pomelo,對pomelo有興趣,但一直遲遲沒有去碰,雖然對pomelo進行源碼分析,在網路上肯定不止我一個,已經有很優秀的前輩走在前面,如http://golanger.cn/,在閱讀Pomelo代碼的時候,已經連載到了11篇了,在我的源碼分析參考了該部落格,當然,也會加入我對pomelo的理解,藉此希望能提高一下自己對node.js的瞭解和學習一些優秀的設計。
開發環境:win7 調試環境:webstorm5.0 node.js版本:v0.8.21 源碼版本package.json:
{"name": "chatofpomelo","version": "0.0.1","private": false,"dependencies": {"pomelo": "0.2.0","log4js": ">= 0.4.1","crc": ">=0.0.1"}}
gameserver/app.js
var pomelo = require('pomelo');var routeUtil = require('./app/util/routeUtil');/** * Init app for client. */var app = pomelo.createApp(); //建立Applicationapp.set('name', 'chatofpomelo'); //設定Application名字// app configure app.configure('production|development', function() { // route configuresapp.route('chat', routeUtil.chat);// filter configuresapp.filter(pomelo.timeout());});// start appapp.start();process.on('uncaughtException', function(err) {console.error(' Caught exception: ' + err.stack);});
注意:在webstorm下調試,可能因為工作目錄的設定原因會導致應用的執行路徑問題,導致無法讀取設定檔,所以需要根據實際情況修改如下
var opt = {'base':'D:\\src\\pomelo\\chatofpomelo\\game-server'}var app = pomelo.createApp(opt);app.set('name', 'chatofpomelo');
opt.base 是你的game-server的實際目錄路徑,具體可以根據自己需要來定製
app.js 是game-server的主要入口,主要負責建立application,讀取設定檔,應用到application設定上,並利用app.start()來執行實際的master,monitor等伺服器的start,對於聊天室程式來說,還要做簡單的路由和過濾設定。
application, 應用的定義、component管理,上下文配置, 這些使pomelo framework的對外介面很簡單, 並且具有松耦合、可插拔架構。
所有伺服器的啟動都是從運行app.js開始。每一個伺服器的啟動都首先建立一個全域唯一的application對象,該對象中掛載了所在伺服器的所有資訊,包括伺服器物理資訊、伺服器邏輯資訊、以及pomelo的組件資訊等。同時,該對象還提供應用管理和配置等基本方法。 在app.js中調用app.start()方法後,application對象首先會通過loadDefaultComponents方法載入預設的組件。
pomelo/lib/pomelo.js
var application = require('./application');Pomelo.createApp = function (opts) { var app = application; app.init(opts); self.app = app; return app;};
pomelo/lib/application.js
/** * Application prototype. * * @module */var Application = module.exports = {};/** * Application states */var STATE_INITED = 1; // app has initedvar STATE_START = 2; // app startvar STATE_STARTED = 3; // app has startedvar STATE_STOPED = 4; // app has stoped/** * Initialize the server. * * - setup default configuration * * @api private */Application.init = function(opts) { opts = opts || {}; logger.info('app.init invoked'); this.loaded = []; this.components = {}; this.settings = {}; // set,和get功能的容器 this.set('base', opts.base); //設定伺服器的工作目錄 this.defaultConfiguration(); //根據設定檔,載入master,monitor等伺服器 this.state = STATE_INITED; //application工作狀態 logger.info('application inited: %j', this.get('serverId'));};
pomelo/lib/application.js
/** * Initialize application configuration. * * @api private */Application.defaultConfiguration = function () { var args = utils.argsInfo(process.argv); this.setupEnv(args); //application環境設定 this.loadServers(); //載入伺服器配置資訊 this.loadConfig('master', this.getBase() + '/config/master.json'); //載入mater伺服器配置資訊 this.processArgs(args); //根據啟動參數設定伺服器配置 this.configLogger();};
utils.argsInfo(process.argv); 擷取系統啟動參數,我們不妨看看到底有哪些參數支援
啟動game-server伺服器:>pomelo start [development | production] [--daemon]
根據args參數設定application的工作環境是development或production
/** * Setup enviroment. * @api private */Application.setupEnv = function(args) { this.set('env', args.env || process.env.NODE_ENV || 'development', true);};
載入伺服器資訊,並且儲存在__serverMap___記憶體下
/** * Load server info from configure file. * * @api private */Application.loadServers = function() { this.loadConfig('servers', this.getBase() + '/config/servers.json'); var servers = this.get('servers'); var serverMap = {}, slist, i, l, server; for(var serverType in servers) { slist = servers[serverType]; for(i=0, l=slist.length; i<l; i++) { server = slist[i]; server.serverType = serverType; serverMap[server.id] = server; } } this.set('__serverMap__', serverMap);};
server.json
{ "development":{ "connector":[ {"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "wsPort":3050}, {"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "wsPort":3051}, {"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "wsPort":3052} ], "chat":[ {"id":"chat-server-1", "host":"127.0.0.1", "port":6050}, {"id":"chat-server-2", "host":"127.0.0.1", "port":6051}, {"id":"chat-server-3", "host":"127.0.0.1", "port":6052} ], "gate":[ {"id": "gate-server-1", "host": "127.0.0.1", "wsPort": 3014}] }, "production":{ "connector":[ {"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "wsPort":3050}, {"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "wsPort":3051}, {"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "wsPort":3052} ], "chat":[ {"id":"chat-server-1", "host":"127.0.0.1", "port":6050}, {"id":"chat-server-2", "host":"127.0.0.1", "port":6051}, {"id":"chat-server-3", "host":"127.0.0.1", "port":6052} ], "gate":[ {"id": "gate-server-1", "host": "127.0.0.1", "wsPort": 3014}] }}
工具函數,讀取json設定檔,在這裡讀取的是master.json檔案
/** * 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 */Application.loadConfig = function (key, val) { var env = this.get('env'); val = require(val); if (val[env]) { val = val[env]; } this.set(key, val);};
master.json
{ "development":{ "id":"master-server-1", "host":"127.0.0.1", "port":3005, "queryPort":3015, "wsPort":2337 }, "production":{ "id":"master-server-1", "host":"127.0.0.1", "port":3005, "queryPort":3015, "wsPort":2337 }}
根據進程讀取配置好的參數,設定管理員。
Application.processArgs = function(args){ var serverType = args.serverType || 'master'; var serverId = args.serverId || this.get('master').id; this.set('main', args.main, true); this.set('serverType', serverType, true); this.set('serverId', serverId, true); if(serverType !== 'master') { this.set('curServer', this.getServerById(serverId), true); } else { this.set('curServer', this.get('master'), true); }};
項目的日誌配置
Application.configLogger = function() { if(process.env.POMELO_LOGGER !== 'off') { log.configure(this, this.getBase() + '/config/log4js.json'); }};
log4js.json
{ "appenders": [ { "type": "file", "filename": "./logs/node-log-${opts:serverId}.log", "fileSize": 1048576, "layout": { "type": "basic" }, "backups": 5 }, { "type": "console" }, { "type": "file", "filename": "./logs/con-log-${opts:serverId}.log", "pattern": "connector", "fileSize": 1048576, "layout": { "type": "basic" } ,"backups": 5, "category":"con-log" }, { "type": "file", "filename": "./logs/rpc-log-${opts:serverId}.log", "fileSize": 1048576, "layout": { "type": "basic" } ,"backups": 5, "category":"rpc-log" }, { "type": "file", "filename": "./logs/forward-log-${opts:serverId}.log", "fileSize": 1048576, "layout": { "type": "basic" } ,"backups": 5, "category":"forward-log" }, { "type": "file", "filename": "./logs/crash.log", "fileSize": 1048576, "layout": { "type": "basic" } ,"backups": 5, "category":"crash-log" } ], "levels": { "rpc-log" : "ERROR", "forward-log": "ERROR" }, "replaceConsole": true}