pinus 学习(一)项目结构与源码分析启动

项目结构介绍,项目入口,服务器启动流程介绍

pinus 学习(一)项目结构与源码分析启动

项目结构介绍,项目入口,服务器启动流程介绍

目录结构

创建一个helloworld示例工程,会创建两个服务器,一个game-server一个web-server,game-server是游戏服务器,目录结构如下

game-server目录

  • 目录app中存放项目代码
  • 目录config中存放服务器配置
  • 目录dist是编译后生成的目录
  • 目录node_modules是第三方库包
  • 文件app.ts是服务器入口
  • 文件copy.js 功能是将配置文件拷贝到dist目录
  • 文件preload.ts实现捕获错误
  • 文件package.js与package-lock.json 是npm包管理配置文件 (依赖的第三方库和自定义脚本)
  • 文件tsconfig.json 是 typescript项目的配置文件(编译的配置与需要编译的文件)

项目结构

创建的helloworld的示例工程的game-server中启动了两个服务器,master服务器和connector服务器

服务器中通过添加组件(component)来增加功能

master服务器

MasterComponent

监控功能,启动了其他服务器

MonitorComponent

监控功能

connector服务器

ProxyComponent

rpc 中 的client,调用其他服务器的函数

RemoteComponent

rpc 中 的server,服务与其他服务器,接收其他服务器的调用

ConnectionComponent

前台服务器才有,管理连接上的用户信息与用户数量

ConnectorComponent

前台服务器才有,管理用户连接,发送与接受消息,(根据配置会决定是否启动DictionaryComponentProtobufComponent

SessionComponent

前台服务器才有,管理sessionID与socket的对应关系,sessionID在connectorComponent中设置

PushSchedulerComponent

前台服务器才有,推送消息

BackendSessionComponent

绑定后台服务器sessionID与(用户和前台服务器)的关系,连接绑定前台服务器与后台服务器的用户

ChannelComponent

将几个用户串成一组,可以群发消息

ServerComponent

服务器功能,其中有Filter(过滤器),Handler(处理某条客户端消息),Crons(一些定时任务)

MonitorComponent

监控功能

项目启动

在game-server目录中执行npm i安装第三方依赖,然后运行tsc,编译TS代码,编译结果将输出到dist文件夹中,然后运行node cooy 拷贝配置文件到dist目录中,再在dist目录中执行node app ,服务器就启动了。

源码分析

app.ts

这个文件是入口,创建项目后默认的代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { pinus } from 'pinus';
import { preload } from './preload';

/**
 *  替换全局Promise
 *  自动解析sourcemap
 *  捕获全局错误
 */
preload();

/**
 * Init app for client.
 */
let app = pinus.createApp();
app.set('name', 'helloworld');

// app configuration
app.configure('production|development', 'connector', function () {
    app.set('connectorConfig',
        {
            connector: pinus.connectors.hybridconnector,
            heartbeat: 3,
            useDict: true,
            useProtobuf: true
        });
});

// start app
app.start();

这里会启动Master服务器,app.configure的第二个参数说明,这个配置是connector服务器的配置,master在这里不会执行, app.start()中会加载Master服务器的两个默认Component,并初始化,然后再根据配置文件servers.json去启动connector服务器

先来看下app.ts里面的内容

let app = pinus.createApp(); 这里创建了Application类,在createApp中调用了init,然后使用app.configure() 来给每个服务器设置配置,添加自定义组件,最后app.start() 启动服务器

pinus.createApp()

创建一个application,其中init函数如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
init(opts ?: ApplicationOptions) {
        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());
    }
    
appUtil.ts
export function defaultConfiguration(app: Application) {
    let args = parseArgs(process.argv);//解析运行命令参数
    setupEnv(app, args);				//设置当前环境(Development或是Production)
    loadMaster(app);					//从config目录中读取master.json配置记录
    loadServers(app);					//从config目录中读取servers.json配置记录
    processArgs(app, args);				//解析命令行参数,这里面只有master服务器可以从命令行中获取参数,其他服务器都会从配置文件中获取参数
    configLogger(app);					//配置日志
    loadLifecycle(app);					//helloworld示例工程中没有,根据后面的代码,猜测是插件的加载(插件在后面介绍,大概是一系列功能的集合)
}

app.configure()

根据传入的env和type来决定是否要执行fn里的内容,helloworld示例工程中的这个函数,只有connector服务器需要执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
configure(fn: ConfigureCallback): Application;
    configure(env: string, fn: ConfigureCallback): Application;
    configure(env: string, type: string, fn: ConfigureCallback): Application;
    configure(env: string | ConfigureCallback, type ?: string | ConfigureCallback, fn ?: ConfigureCallback): Application {
        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 as string)) {
            if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type as string)) {
                fn.call(this);
            }
        }
        return this;
    }

app.start()

读取完配置后,这里正式启动服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
start(cb ?: (err ?: Error, result ?: void) => void) {
        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();
                }
            });
        });
    }

appUtil.startByType,这个函数可以根据配置决定是否通过master服务器来启动其他服务器,如果不通过master服务器启动,会直接在这里启动服务,start()后面的逻辑也不会执行了,

如果通过Master服务器启动,会执行回调函数,回调函数中appUtil.loadDefaultComponents(self); 加载了默认的组件,项目结构中介绍了的组件全部是默认组件,connector服务器的组件就是非master服务器的默认组件。

加载默认组件会直接创建出来,调用构造函数。

下面通过appUtil.optLifecycles 执行每个插件的beforeStartup,然后再通过appUtil.optComponents 执行每个组件的beforeStart与start,然后执行 self.afterStart(cb);,afterStart里面将执行每个组件的afterStart和每个插件的afterStartup

最后发送start_server信号,一个服务器就启动了

组件和插件中的afterStartAll,将会再所有服务器启动后执行,这是单个服务器启动的逻辑,所以没有调用

最后更新于 May 28, 2024 00:00 UTC
人生乱套,我大叫
使用 Hugo 构建
主题 StackJimmy 设计