eggjs是什么
为企业级框架和应用而生
1、专注于提供 Web 开发的核心功能和一套灵活可扩展的插件机制
2、Egg 奉行『约定优于配置』:没有约定的团队,沟通成本是非常高的,比如有人会按目录分栈而其他人按目录分功能,开发者认知不一致很容易犯错
特性
多进程管理
我们知道 JavaScript 代码是运行在单线程上的,换句话说一个 Node.js 进程只能运行在一个 CPU 上。那么如果用 Node.js 来做 Web Server,就无法享受到多核运算的好处。作为企业级的解决方案,我们要解决的一个问题就是:
如何榨干服务器资源,利用上多核 CPU 的并发优势?
- Worker 进程异常退出以后该如何处理?
- 多个 Worker 进程之间如何共享资源?
- 多个 Worker 进程之间如何调度?
agent
- Master 启动后先 fork Agent 进程
- Agent 初始化成功后,通过 IPC 通道通知 Master
- Master 再 fork 多个 App Worker
- App Worker 初始化成功,通知 Master
- 所有的进程初始化成功后,Master 通知 Agent 和 Worker 应用启动成功
通信
-
app.messenger.broadcast(action, data)
:发送给所有的 agent / app 进程(包括自己) -
app.messenger.sendToApp(action, data)
: 发送给所有的 app 进程- 在 app 上调用该方法会发送给自己和其他的 app 进程
- 在 agent 上调用该方法会发送给所有的 app 进程
-
app.messenger.sendToAgent(action, data)
: 发送给 agent 进程- 在 app 上调用该方法会发送给 agent 进程
- 在 agent 上调用该方法会发送给 agent 自己
-
agent.messenger.sendRandom(action, data)
:- app 上没有该方法(现在 Egg 的实现是等同于 sentToAgent)
- agent 会随机发送消息给一个 app 进程(由 master 来控制发送给谁)
-
app.messenger.sendTo(pid, action, data)
: 发送给指定进程 -
app.messenger.on(action, data)
// 发消息
// app.js
module.exports = app => {
// 注意,只有在 egg-ready 事件拿到之后才能发送消息
app.messenger.once('egg-ready', () => {
app.messenger.sendToAgent('agent-event', { foo: 'bar' });
app.messenger.sendToApp('app-event', { foo: 'bar' });
});
}
// 收消息
app.messenger.on(action, data => {
// process data
});
app.messenger.once(action, data => {
// process data
});
扩展extend
框架提供了多种扩展点扩展自身的功能:
- Application
- Context
- Request
- Response
- Helper
插件开发plugin
{
"name": "egg-rpc",
"eggPlugin": {
"name": "rpc",
"dependencies": [ "registry" ],
"optionalDependencies": [ "vip" ],
"env": [ "local", "test", "unittest", "prod" ]
}
}
{String} name
- 插件名(必须配置),具有唯一性,配置依赖关系时会指定依赖插件的 name。{Array} dependencies
- 当前插件强依赖的插件列表(如果依赖的插件没找到,应用启动失败)。{Array} optionalDependencies
- 当前插件的可选依赖插件列表(如果依赖的插件未开启,只会 warning,不会影响应用启动)。{Array} env
- 只有在指定运行环境才能开启,具体有哪些环境可以参考运行环境。此配置是可选的,一般情况下都不需要配置。
定制上层框架framework
{
"name": "framework-example",
"version": "1.0.0",
"dependencies": {
"egg": "^2.31.0",
"yadan": "^2.0.0"
},
"devDependencies": {
"egg-bin": "^4.16.4",
"egg-mock": "^3.26.0"
},
"scripts": {
"dev": "egg-bin dev",
"test": "egg-bin test"
},
"egg": {
"framework": "yadan"
}
}
渐进式开发
- 最初始的状态
- 插件的雏形
- 抽成独立插件
- 沉淀到框架
说明:
- 一般来说,当应用中有可能会复用到的代码时,直接放到
lib/plugin
目录去,如例子中的egg-ua
。 - 当该插件功能稳定后,即可独立出来作为一个
node module
。 - 如此以往,应用中相对复用性较强的代码都会逐渐独立为单独的插件。
- 当你的应用逐渐进化到针对某类业务场景的解决方案时,将其抽象为独立的 framework 进行发布。
- 当在新项目中抽象出的插件,下沉集成到框架后,其他项目只需要简单的重新
npm install
下就可以使用上,对整个团队的效率有极大的提升。
eggjs 启动流程
框架启动在多进程模型、Loader、插件中或多或少都提过,这里系统的梳理下启动顺序。
- startCluster 启动传入
baseDir
和framework
,Master 进程启动 - Master 先 fork Agent Worker
-
- 根据 framework 找到框架目录,实例化该框架的 Agent 类
- Agent 找到定义的 AgentWorkerLoader,开始进行加载
- AgentWorkerLoader,开始进行加载 整个加载过程是同步的,按 plugin > config > extend >
agent.js
> 其他文件顺序加载 agent.js
可自定义初始化,支持异步启动,如果定义了 beforeStart 会等待执行完成之后通知 Master 启动完成。
- Master 得到 Agent Worker 启动成功的消息,使用 cluster fork App Worker
-
- App Worker 有多个进程,所以这几个进程是并行启动的,但执行逻辑是一致的
- 单个 App Worker 和 Agent 类似,通过 framework 找到框架目录,实例化该框架的 Application 类
- Application 找到 AppWorkerLoader,开始进行加载,顺序也是类似的,会异步等待,完成后通知 Master 启动完成
- Master 等待多个 App Worker 的成功消息后启动完成,能对外提供服务。
初始化项目
hello word
npm init egg --type=simple
eggjs的部署
写在最后
eggjs 框架是一个适合 nodejs 企业级生产环境中使用,可以应对大多数业务场景
eggjs源码:github.com/eggjs/egg