千里之行始於足下,一直說想了解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(); //創建Application
app.set('name', 'chatofpomelo'); //設置Application名字
// app configure
app.configure('production|development', function() {
// route configures
app.route('chat', routeUtil.chat);
// filter configures
app.filter(pomelo.timeout());
});
// start app
app.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 inited
var STATE_START = 2; // app start
var STATE_STARTED = 3; // app has started
var 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);
};
{
"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
}