eggjs工程与分析

2,111 阅读7分钟
原文链接: mp.weixin.qq.com

本文大量引用了eggjs官方文档指引,并且根据文档创建了eggjs项目,进行了简单的分析,这仅是我个人的总结,轻拍。

根据公开的文献,eggjs的前身属于蚂蚁金服,于2016年9月亮相JSConf China并正式开源。它有一个很显著的特点:【微内核+插件机制】,专门为团队架构师提供框架定制的能力,可以基于eggjs来定制业务框架。

让天下没有难用的Node.js

快速入门

$ npm i egg-init -g
$ egg-init egg-simple --type=simple
$ cd egg-simple

eggjs提供了一个命令行工具,来初始化工程,根据官网提供的信息,你可以看见整个工程的分布,每一个目录都是有明确意义的。

egg-project
├── package.json  // 定义了项目所需要的各种模块,以及配置信息(名称、版本等元数据)
├── app.js (可选)   // 用于自定义初始化工作,这个文件只能返回一个函数beforeStart
├── agent.js (可选)  // 跟app.js类似。
├── app
 |   ├── router.js  // 用于配置URL路由规则。
│   ├── controller  // 对用户的请求参数进行处理,调用对应的service方法处理业务,返回结果
│   |   └── home.js
│   ├── service (可选)  // 复杂数据的处理,第三方服务的调用。
│   |   └── user.js
│   ├── middleware (可选)  // 用于编写中间件
│   |   └── response_time.js
│   ├── schedule (可选)  // 用于编写定时任务
│   |   └── my_task.js
│   ├── public (可选)  // 存放静态资源
│   |   └── reset.css
│   ├── view (可选)  // 用于放置模板文件
│   |   └── home.tpl
│   └── extend (可选)  // 用于框架的扩展
│       ├── helper.js (可选)
│       ├── request.js (可选)
│       ├── response.js (可选)
│       ├── context.js (可选)
│       ├── application.js (可选)
│       └── agent.js (可选)
├── config   
 |   ├── plugin.js  //  用于配置需要加载的插件
|   ├── config.default.js  // 默认配置文件
|   ├── config.prod.js  // 生产环境配置文件
|   ├── config.test.js (可选)
|   ├── config.local.js (可选)
|   └── config.unittest.js (可选)
└── test   //  单元测试
    ├── middleware
    |   └── response_time.test.js
    └── controller
        └── home.test.js

工程分析

正常情况下,我们几乎最常用的内置对象或者配置,一个内置对象描述了一个请求的生命周期,它在一个controller对象的上下文中可访问。

  • Application,一个应用只会实例化一个,在它的上面可以挂载一些全局方法或属性

  • Context,请求级别对象,一个请求的生命周期内,框架会实例化一个Context,这个对象封装了这次请求的所有信息

  • Request,提供了一系列的方法来获取HTTP的信息

  • Response,提供了一系列的方法来设置HTTP的信息

由于eggjs也是基于Koa来进行的封装,所以洋葱模型能支持的特性,在eggjs中同样支持。Koa的设计思路是为中间件提供一系列的语法糖,来增强其互用性和健壮性,但有时候中间件并不能提供所有的能力。eggjs提供的插件机制,就很好的辅助解决了这个问题。再这些跟请求相关的对象之外,eggjs对开发模型也进行了封装。

  • Controller,框架提供了一个Controller基类,并且约定放置在app/controller目录下,这一层主要是对用户请求参数进行处理,Controller的上下文中挂载了很多有用的对象,因此推荐所有Controller都继承该基类。

  • Service,框架提供了一个Service基类,并且约定放置在app/service目录下,这一层主要解决在复杂业务场景下对逻辑的封装,比如查询数据库,调用第三方服务(Github登录),因此推荐所有Service都继承该基类。

  • Helper: 用来提供一些使用的实用函数。可以将一些常用的动作抽象为helper.js里面的一个独立函数。Helper跟Controller基类有一样的属性,也会在每次请求时实例化。

  • Config: 所有框架、插件和应用级别的配置都可以通过Config对象获取到。

  • Logger: 日志

框架提供了强大可扩展的配置功能,可以自动Merge插件,应用,框架的配置,并且按顺序覆盖,这是一个很有用的功能,可以很容易的配置你的Web应用。

在此之外,我们可以全面窥视一下eggjs能支持的其他能力

  • Middleware,我们约定一个中间件放置在app/middleware目录下,如果你想它的能力可以应用在整个Web应用中,那么就需要配置在config.xxx.js文件中。如果仅仅是应用在请求级别上,那么需要在具体的路由上进行配置

  • Router,它主要是来约定描述请求URL和具体承担执行动作的controller,框架约定了app/router.js来描述统一的路由贵族。

  • 定时任务,它主要是来处理一些需要定时的任务处理,框架约定放置在app/schedule目录下

  • 单元测试,这一层的目的是对逻辑的测试检验,提供健壮性的验证,框架约定放置在app/test目录下

  • 框架的扩展能力,它指明了Application,Context,Request,Response,Helper是可以进行扩展的,我们可以增加这些对象的api,来进一步加强框架的能力

在整套框架中我很看重安全性,eggjs总结阿里巴巴的很多经验,并且默认提供了这样的支持,这一点很帮,节省了一些考虑这方面的时间,框架本身针对 Web 端常见的安全风险内置了丰富的解决方案。

  • 利用 extend 机制扩展了 Helper API, 提供了各种模板过滤函数,防止钓鱼或 XSS 攻击

  • 常见 Web 安全头的支持。可以针对某一路径开启或忽略安全校验

  • CSRF 的防御方案

  • 灵活的安全配置,可以匹配不同的请求 url

  • 可定制的白名单,用于安全跳转和 url 过滤

  • 各种模板相关的工具函数做预处理

  • 在框架中内置了安全插件 egg-security, 提供了默认的安全实践

框架简单分析

阅读到这里,其实大家基本上对eggjs有了一个大概的应用,这里的描述主要是简单分析一下eggjs的运行机制,它有助于我们理解eggjs框架,独创的Loader机制实现了加载的约定,并且抽象了很多底层api可以进一步的扩展,这样的加载机制,几乎做到了无感知。

  • 按插件 => 框架 => 应用依次加载

  • 插件之间的顺序由依赖关系决定,被依赖方先加载,无依赖按 object key 配置顺序加载,具体可以查看插件章节

  • 框架按继承顺序加载,越底层越先加载

如果你要真正的部署Web应用,由于JavaScript的单线程特点,注定无法享有多核运算的好处,但是Node.js提供了Cluster模块,它要解决的问题,主要就是来享用多核运算的好处,我们可以简单的看一下Cluster模块的功能:

  • 在服务器上同时启动多个进程

  • 每个进程里都跑的是同一份源代码(好比把以前一个进程的工作分给多个进程去做)

  • 更神奇的是,这些进程可以同时监听一个端口

这里有一个很有意思的特点,启动一个Master进程来负责启动其他进程,其他进程类比Worker,Worker进程来接收请求,对外提供服务,每一个Worker之间的通信,都需要借助Master,由Master来转发。(额外话题,这里是不是和React的兄弟组件通信很像,都需要一个Master来做中转)

虽然看起来,很容易,其实实现起来很多细节问题,要处理。

最后,如果你开发Node.js Web应用,我强烈推荐使用eggjs,这是一个很值得学习的Web基础框架。


长按二维码关注公众号:

你也可以关注我的新浪微博,搜索i_icepy,很期待和大家交流