pomelo启动都干了什么
这是pomelo源码分析的最后一篇文章。讲一下典型的一个pomelo引擎做了什么
从哪里启动的
我们知道一个pomelo启动是这样子的pomelo start
可以看下电脑的环境变量,最终执行文件在pomelo/bin/pomelo
program.command('start')
.description('start the application')
.option('-e, --env <env>', 'the used environment', DEFAULT_ENV)
.option('-D, --daemon', 'enable the daemon start')
.option('-d, --directory, <directory>', 'the code directory', DEFAULT_GAME_SERVER_DIR)
.option('-t, --type <server-type>,', 'start server type')
.option('-i, --id <server-id>', 'start server id')
.action(function(opts) {
start(opts);
});
写入一些启动参数,这里只是写入了执行环境以及目录地址
在start函数中很简单,检测一些文件。
写入执行环境以及服务器类型,然后启动目录下的app.js文件
转过来看app.js干了什么
第一行var pomelo = require(‘pomelo’);
生成一个pomelo对象,pomelo对象有什么
createApp做了什么
生成pomelo对象后,紧接着创建了一个app
var app = pomelo.createApp();
跳转到application.js中生成一个实体。
在Application.init中存有一些内部变量可以自己研究,这里分析下appUtil.defaultConfiguration(this);
module.exports.defaultConfiguration = function(app) {
var args = parseArgs(process.argv);
setupEnv(app, args);
loadMaster(app);
loadServers(app);
processArgs(app, args);
configLogger(app);
loadLifecycle(app);
};
setupEnv(app, args); // 根据启动参数设置环境变量
loadMaster(app); // 载入master配置 /config/master.json存入app.master
中
loadServers(app); // 载入servers配置 /config/servers.json存入app.__serverMap__
中
processArgs(app, args); // 根据启动参数设置到app中的属性中。这里需要注意下,启动参数中如果没有写入serverId,默认会是master的id,也就是第一个进程启动的是master
configLogger(app); // log配置
oadLifecycle(app); // 启动进程的生命期 读取app/servers/serverName/lifecycle.js,对进程启动前,启动后,关闭可以触发回调函数
configure做了什么
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;
};
可以看到,通过传入的env或者type,来决定是否执行传入的回调函数
转过来看app.js中的代码
app.configure('production|development', 'connector', function(){
app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true
});
});
这里就是当环境为’production|development’且进程为’connector’执行里面的函数
这里可以处理特定进程的一些特定操作。这个代码中就是设置了一些connector进程的’connectorConfig’参数,这个参数干什么的,往下看。
start做了什么
app.start();
在 Application.start中,我们看下。
appUtil.startByType
:
这里是根据传入的参数启动,startId和type
在本例中startByType仅仅是执行回调。
appUtil.loadDefaultComponents(self)
:
这里可以看到。因为是master进程,所以取了masterConfig属性,当作参数生成一个master组件
组件看这里
然后启动了一个monitor组件,没错master进程做到了自己监控自己
在Application.load函数中,会把加载的组件写入this.loaded
中
接下来是调用进程的生命期的before函数,之后执行startUp
:
appUtil.optComponents
:
在这里执行各个组件的start函数,正式启动组件
然后就是进入Application.afterStart
:
先执行各个组件的‘afterStart’函数,然后是进程生命期的after
其他进程怎么启动的?
在master组件启动中pomelo/lib/master/master.js start
:
if(self.app.get(Constants.RESERVED.MODE) !== Constants.RESERVED.STAND_ALONE) {
starter.runServers(self.app);
}
当目前的模式不是单进程模式时,会调用一次runServers
starter.runServers = function(app) {
var server, servers;
var condition = app.startId || app.type;
switch(condition) {
case Constants.RESERVED.MASTER:
break;
case Constants.RESERVED.ALL:
servers = app.getServersFromConfig();
for (var serverId in servers) {
this.run(app, servers[serverId]);
}
break;
default:
server = app.getServerFromConfig(condition);
if(!!server) {
this.run(app, server);
} else {
servers = app.get(Constants.RESERVED.SERVERS)[condition];
for(var i=0; i<servers.length; i++) {
this.run(app, servers[i]);
}
}
}
};
我们第一次启动时基本也没什么参数,可以自己打断点看下参数。这里的condition=ALL
拉去server的所有配置,然后run一下
在run里面的,设置了一堆奇奇怪怪的参数,然后启动进程,依旧是执行app.js
其他进程的start做了什么
configure不说了,就是一些特殊操作,本例中就是设置了’connectorConfig’属性
appUtil.startByType
:
仅仅是回调,同上
appUtil.loadDefaultComponents(self)
:
在这里可以看到,对于非master进程来说,启动了很多的组件。前端和后端启动的也有点差别。
注意:在加载组件的时候,入参就是设置在app上的属性。例如本例中在app.configure中设置了‘connectorConfig’属性,这个属性在app.load(pomelo.connector, app.get('connectorConfig'));
这里就用到了。
所以说当你需要一些特殊处理的组件时,可以在app.configure中做对应的设置
appUtil.optComponents
:
启动加载好的组件。
总结
pomelo的整体源码分析已经完成了。这种组件化的方式挺好的。js语言比较简单,没有奇奇怪怪的黑魔法,所以整体看下来挺顺畅的。
复杂的就是通信的组件,因为兼容了几种方式。