EggJS源码阅读-启动流程与代码实现
egg是阿里开源的一个框架,为企业级框架和应用而生,相较于express和koa,有更加严格的目录结构和规范。不同的团队可以基于egg,根据自己的需求封装出适合团队业务的更上层框架。本文对 egg.js 源码进行一个简要的入门阅读和解析。
egg如何启动

根据package.json中的script命令,可以看到执行的直接是egg-bin dev的命令。找到egg-bin文件夹中的dev.js,会看到里面会去执行外层的start-cluster文件:
1 | require(options.framework).startCluster(options); |
此处的options.framework其实指的就是egg框架,然后调用了egg-cluster包中的startCluster方法,egg正式迈出了启动的第一步。
[源码流程]
egg框架采用了master-agent-worder的集群模式,如果所示,官方文档中也对于这三种进程的启动和通信给出了较为详细的说明:
- Master 启动后先 fork Agent 进程
- Agent 初始化成功后,通过 IPC 通道通知 Master
- Master 再 fork 多个 App Worker
- App Worker 初始化成功,通知 Master
- 所有的进程初始化成功后,Master 通知 Agent 和 Worker 应用启动成功
我们尝试从源码中大致将这个流程实现出来:
1 | // egg-cluster/lib/master.js |
[源码流程]
Agent与AppWorker的实现
AgentWorker与AppWorker进程的启动

在启动AgentWorker和AppWorker时,会分别加载agent_worker.js和app_worker.js两个文件并创建进程,其中agent_worker.js中会创建Agent类的实例,而app_worker.js中会创建Application类的实例。
两种进程在启动时都会调用this.loader.load()方法来加载自己相应的一些插件和自定义的扩展。

基于egg_loader实现了AppWorkerLoader和 AgentWorkerLoader,上层框架基于这两个类来扩展,Loader的扩展只能在框架进行。
1 | import { AppWorkerLoader } from 'egg' |
Agent如何实现

Agent对象在egg-cluster创建环节中被创建出来,继承自egg.Agent对象,该对象继承EggApplication,且loader为./lib/loader/agent_worker_loader.js文件,继承自egg-core.eggLoader对象,整体继承链如上图。
1 | // egg/lib/agent.js |
Agent类的实现中,主要是实例化了EggApplication,调用了this.loader.load()方法来加载各种文件和配置。
1 | // egg/lib/egg.js |
在EggApplication类的实现中,我们可以看到是继承自EggCore类,在父类构建好之后,会调用this.loader.loadConfig()方法,该方法的loader实例实际指向了AgentWorkerLoader(agent_worker_loader.js):
1 | // egg/lib/loader/agent_worker_loader.js |
loadPlugin方法会加载三种插件:
eggPlugins,从eggjs框架配置的插件,也就是egg/config/plugins.js文件中egg框架自带的插件;appPlugins,每个应用自己配置的插件,也就是myproject/config/plugins.js,用户可以自定义配置一些特殊的插件;customPlugins,应用启动命令中参数EGG_PLUGINS值所代表的插件;
最后会将这三种插件都挂在app实例上:this.plugins = enablePlugins;
loadConfig方法会加载三种配置:
appConfig,每个应用自己独有的配置,其中会按顺序加载两个配置,一个是默认配置config.default,另一个是当前环境的配置config.${this.serverEnv},也就是myproject/config下的一些配置文件加载- 加载自定义添加的
plugin插件的配置 - 加载框架
egg的配置,即egg/config - 重新加载应用
app的配置,即myproject/config下的
最后将合并的配置挂载在app实例上this.config = target;
Application如何实现
上面提到,AppWorker在实例化的过程中,会调用this.loader.load()。进入具体这个Application所对应的loader.load方法,可以发现Application的实现比Agent的实现多调用了很多加载的方法:
this.loadApplicationExtend();,该方法的调用会给应用加载扩展方法,加载路径为app\extend\application.js, 会将对应的对象挂载在app 应用上。this.loadRequestExtend();,加载app\extend\request.jsthis.loadResponseExtend();,加载app\extend\response.jsthis.loadContextExtend();,加载app\extend\context.jsthis.loadHelperExtend();,加载app\extend\helper.jsthis.loadService()this.loadMiddleware()this.loadController()this.loadRouter()
总结
egg启动服务集群,采用了
master-agent-worker模式,AgentWorker和AppWorker都由Application(egg/lib/applicaton.js) -> EggApplication(egg/lib/egg.js) -> EggCore(egg-core/lib/egg.js) -> KoaApplication(koa)原型链进行继承Agent和Application在实例化的过程中,都会调用相应的Loader去加载自己所需的插件和配置,且加载顺序严格按照插件plugin-框架framework-应用application这样一个顺序
