egg.js遵守的规范
- 目录结构和加载机制规范
- 配置文件约定规范
- 插件机制
- 多进程和进程通信模型
目录结构和加载机制规范
目录结构规范
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app
| ├── router.js
│ ├── controller
│ │ └── home.js
| ├── extend (可选,对 egg 的扩展)
│ | ├── helper.js (可选)
│ | ├── filter.js (可选)
│ | ├── request.js (可选)
│ | ├── response.js (可选)
│ | ├── context.js (可选)
│ | ├── application.js (可选)
│ | └── agent.js (可选)
│ ├── service (可选)
| ├── public (可选)
| │ ├── favicon.ico
| | └── ...
│ ├── middleware (可选)
│ │ └── response_time.js
│ └── views (可选,由 view 插件规范,建议统一为 views)
| ├── layout.html
│ └── home.html
├── config
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| ├── config.unittest.js (可选)
│ ├── plugin.js
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
package.json
每个应用必须包含package.json。
每个package.json至少包含下面配置项目
- name: 表示当前应用名称
- engines: 用来表示当前应用所依赖的Node版本,这样能确保任何一个开发者,只需要通过 tnpm install 就能将应用的 所有环境依赖安装上了,包括 Node 本身。 通过 install-node 字段知道 Node 的版本。
"engines": {
"install-node": "^8"
}
app目录
- app/router.js
app/router.js 是应用的路由配置文件,所有路由配置都在此设置, 放在同一个文件非常方便通过 url 查找到对应的 controller 代码 - app/controller
每个 app/controller/.js 文件,都会被自动加载到 app.controller. 上。 这样就能在 app/router.js 里面方便地进行路由配置。
以下目录将按约定加载:
├── app
└── controller
├── foo_bar (会自动将文件命名转换成驼峰命名法, foo_bar => fooBar, foo-bar-ok => fooBarOk)
| └── user.js ==> app.controller.fooBar.user
├── blog.js ==> app.controller.blog
controller 是一个 HTTP 请求链中最后的一个处理者, 按约定不太可能一个 HTTP 请求会经过 2 个 controller 的。
在 controller 中可以调用 service 等依赖目录。
- app/middleware
应用自定义中间件都放在此目录,然后需要在 config/config.js 配置中间件的启动顺序。
// config/config.js
exports.middleware = [
'responseTime',
'locals',
];
通常,middleware是每一个http请求都会经过,所以开发者需要明确了解自己开发的中间件前后顺序关系。
- app/service
数据服务逻辑层的抽象,具体的业务实现代码一般在这里。
app/service/*.js
下的文件,每个 Service 都会像 Context 一样,在 每个请求生成的时候,被自动实例化并且放到ctx.service
下
├── app
└── service
├── foo_bar (会自动将文件命名转换成驼峰命名法, foo_bar => fooBar, foo-bar-ok => fooBarOk)
| └── user.js ==> ctx.service.fooBar.user
├── blog.js ==> ctx.service.blog
- app/views
存放模板文件和只在客户端使用的脚本目录文件。 - app/public 静态资源目录
存放前端的静态资源文件,会自动做一下映射
app/public/js/main.js => /public/js/main.js
app/public/styles/blue.css => /public/styles/blue.css
-
app.js
用于在启动时候做一些初始化的工作。一般来说:大部分的应用不需要使用此功能。如果一个应用需要在服务启动时候进行依赖检查。需要通过app.js
来实现 -
agent.js
和app.js
类似,在Agent 进程中,如果需要做一些自定义的处理,可以在这个文件实现。 -
扩展
在 extend 目录下都是对已有 API 进行扩展,也就是追加到 prototype 上,如 extend/application.js 是扩展 Application.prototype。
app/extend/request.js
: 扩展 koa request
app/extend/response.js
: 扩展 koa response
app/extend/context.js
: 扩展 koa context
app/extend/application.js
: 扩展 koa application
app/extend/agent.js
: 扩展 agent 对象 -
配置文件约定
- 运行环境约定
| name | NODE_ENV | description |
| --- | --- | --- |
| pre | production | 预发环境 |
| prod | production | 生产环境 |
| test | production | 交付测试环境,即常说的 sit 环境 |
| default | production | 开发服务器,通常每次项目迭代都会自动申请开发服务器 |
| local | development or 空 | 本地开发环境,即开发者的电脑,开发者很可能同时开发多个应用 |
| unittest | test | 单元测试运行环境,如开发者本地,ci 环境等等 |
插件机制
插件最终也是为了复用和渐进式开发形成框架。插件和中间件不同:中间件是request和response的模型,插件可以认为是一个小型的应用。
插件能做什么
一个插件就是一个mini应用,它是对应用的扩展,但它不包含controller 和 router。
- 对Koa扩展,可以通过 app/extend/request.js, response.js, context.js, application.js 实现。
- 自定义中间件,可以结合app.js 和 app/middleware/*.js 实现。
- 实现启动依赖进行检查,通过 app.js 里面使用约定的 app.readyCallback(asyncName) 实现。
在 package.json 中定义插件的属性
"eggPlugin": {
"name": "sequelize"
},
install-node 机制
在package.json
里面的engines
配置上install-node:"$version"
可以指定当前应用所使用的node版本,npm 会自动安装上这个 node 版本, 这样就能保证所有开发者和所有运行环境都是同一个 node 版本了。
{
"engines": {
"install-node": "4.2.1"
}
}
多进程和进程间通信

master&worker进程
为了最大限度榨干服务器资源,master 进程利用 cluster 根据 CPU 个数启动多个 worker 进程,以达到更好的吞吐率。
agent进程
对于一些公共资源的访问,通用性的操作,如:本地文件监听,配置中心等。每个 worker 都来一遍非常浪费,且会引发各种问题。所以引入agent进程,由master使用child_process
启动,它是一个任务执行进程,并不对外提供http访问。worker进程如果有需要把这部分任务交给agent进程执行,agent执行后告诉worker。
不管对于插件还是应用来说,都可以通过在项目根目录放置agent.js
,在agent进程中执行任务。
进程间通信

- agent由master使用child_process启动,worker由master使用cluster启动。所以
master<->agent
,master<->worker
都可以使用 node 内置的 IPC 通道进行通讯。 - 在应用运行时,发生最多的是agent和worker之间通信,由master转发消息完成,实现了一个虚拟的通道。
messager.broadcast('msg from agent');
messager.on('msg form worker', callback);
健壮性
- master进程健壮性要求比较高,绝对不能挂掉。在master进程不做任何业务代码执行
- agent进程会执行公共资源访问类操作,worker非常需要它,所以 master 进程需要负责 agent 生命周期管理,包括启动和挂掉重启等。
- worker进程是直接对外提供服务的进程,master进程同样负责worker进程的生命周期管理,包括启动和挂掉重启。
网友评论