Egg 框架深度指南:基于 Node.js 与 Koa 的企业级框架构建引擎
Egg 框架深度指南基于 Node.js 与 Koa 的企业级框架构建引擎【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa项目地址: https://gitcode.com/gh_mirrors/egg11/eggEgg 是一个面向企业级应用与框架的 Node.js Web 框架它站在 Koa 之上将进程管理、插件系统、框架定制与约定式加载等能力内置一体帮助开发者从零开始快速搭建可扩展、可复用的后端服务。读完本文你将掌握 Egg 的安装与快速启动方式、四大核心特性内置进程管理、插件系统、框架定制、丰富的官方插件、约定式目录结构背后的加载原理并能基于当前仓库源码定位到每一处特性的实现位置。项目定位与设计理念Egg 在 package.json 中的自我描述是 “A web frameworks framework for Node.js”即“用于构建框架的框架”。这与 README 中 “Born to build better enterprise frameworks and apps” 的标语一脉相承Egg 的目标不只是提供一个开箱即用的 Web 框架更是一套可以在此基础上二次封装、孵化出各种企业级框架与应用的基座。从技术栈上看Egg 深度复用 Koa 的 Application / Context / Request / Response 模型并通过 egg-core 直接继承egg-core的EggCorelib/application.js 又继承EggApplication形成了EggCore - EggApplication - Application的清晰继承链。安装与运行环境要求Egg 的安装极其简单直接通过 npm 安装即可$ npm install egg --save需要注意的是Egg 要求Node.js 6.0.0对应 package.json 中engines.node字段的约束。此外由于 Egg 在 ES6/ES7 时代设计异步编程模型同时支持 Generator co 与 async/await 两种写法社区文档中也提供了从 Generator 迁移到 async function 的完整教程见 async-function.md 与 async-function.md。框架依赖构成通过 package.json 的依赖列表可以看出 Egg 的整体骨架其中几组依赖值得关注运行时核心egg-core加载器与 Application 基类、egg-cluster多进程管理与启动、egg-logger日志体系、egg-cookiesCookie 编解码内置能力插件egg-onerror错误处理、egg-security安全防护、egg-session会话、egg-i18n国际化、egg-schedule定时任务、egg-static静态资源、egg-view视图渲染、egg-multipart文件上传、egg-watcher文件监听、egg-logrotator日志切割、egg-jsonpJSONP 支持等HTTP 客户端urllib、agentkeepalive以及封装在其上的HttpClient。这些插件在 config/plugin.js 中统一声明并默认启用下文会详细展开。四大核心特性README 明确列出 Egg 的四个核心特性这也是理解 Egg 设计哲学的钥匙。内置进程管理Built-in process managementEgg 默认采用 Master / Agent / Worker 的多进程架构这一能力由egg-cluster提供。在 index.js 中可以看到入口导出exports.startCluster require(egg-cluster).startCluster;startCluster会启动一个 Master 进程再由 Master 拉起一个 Agent 进程负责公共资源的后台任务和多个 App Worker 进程负责对外提供服务。这种架构的好处是充分利用多核 CPUWorker 进程数默认与 CPU 核数相关Agent 进程承担数据库连接、定时任务等公共职责避免在每个 Worker 中重复创建通过 lib/core/messenger.js 提供的 Messenger 实现进程间通信IPC。在仓库中Agent 进程的单例实现在 lib/agent.js它继承EggApplication并注册了uncaughtException处理与消息转发包装broadcast、sendTo、sendToApp、sendToAgent、sendRandom以保证在服务启动完成前不会误发消息。应用进程的单例实现在 lib/application.js它在server事件触发后通过graceful库接管优雅退出逻辑并处理clientError对非法请求直接返回 400 Bad Request见DEFAULT_BAD_REQUEST_RESPONSE。插件系统Plugin systemEgg 的插件系统是其可扩展性的根基。框架内置插件在 config/plugin.js 中以{ enable, package }的形式声明例如module.exports { onerror: { enable: true, package: egg-onerror }, session: { enable: true, package: egg-session }, i18n: { enable: true, package: egg-i18n }, watcher: { enable: true, package: egg-watcher }, multipart: { enable: true, package: egg-multipart }, security: { enable: true, package: egg-security }, development: { enable: true, package: egg-development }, logrotator: { enable: true, package: egg-logrotator }, schedule: { enable: true, package: egg-schedule }, static: { enable: true, package: egg-static }, jsonp: { enable: true, package: egg-jsonp }, view: { enable: true, package: egg-view }, };应用自身的插件则写在config/plugin.js应用根目录下的同名文件中同样可以配置enable、package、env等字段。插件加载发生在配置加载之前这一点在 lib/loader/app_worker_loader.js 的loadConfig()方法中有明确体现loadConfig() { this.loadPlugin(); super.loadConfig(); }即先加载插件再加载配置——因为插件的配置必须合并进最终的app.config。加载完成后Egg 会在启动日志中输出已启用插件列表见 lib/egg.js 中的this.loader.orderPlugins。框架定制Framework customizationEgg 可以被二次封装成“框架的框架”。其定制入口是egg#loader与egg#eggPath两个 Symbollib/application.js 中定义了get [EGG_LOADER]() { return AppWorkerLoader; }允许上层框架替换默认加载器get [EGG_PATH]() { return path.join(__dirname, ..); }则指向 Egg 自身安装路径便于加载器定位框架内置目录。上层框架只需在自己的入口中继承Application并覆盖[EGG_LOADER]即可扩展加载逻辑。仓库测试中的 aliyun-egg 就是这样一个自定义框架样例包含lib/aliyun-egg.js、lib/agent.js与自定义插件目录而 custom-egg 则展示了最小化的自定义框架形态。自定义框架的完整指南可以参考 framework.md。同时Egg 通过 index.js 导出一系列可覆盖的基础类方便框架与业务扩展exports.Application require(./lib/application); exports.Agent require(./lib/agent); exports.AppWorkerLoader require(./lib/loader).AppWorkerLoader; exports.AgentWorkerLoader require(./lib/loader).AgentWorkerLoader; exports.Controller require(./lib/core/base_context_class); exports.Service require(./lib/core/base_context_class); exports.Subscription require(./lib/core/base_context_class); exports.BaseContextClass require(./lib/core/base_context_class);这些导出在 test/index.test.js 中通过assert.deepEqual(Object.keys(egg).sort(), [...])被完整验证。丰富的官方插件生态Lots of pluginsREADME 强调 Egg 拥有大量插件可通过egg-plugin主题搜索。除了上述内置插件外社区还维护了大量业务插件数据库、缓存、队列、模板引擎等并允许插件间声明依赖关系。仓库中的 plugin.md 详细介绍了插件的开发与发布规范loader-plugin-dep 等测试 fixture 则验证了插件依赖dep的解析逻辑。快速开始从零启动一个 Egg 应用README 给出了标准的快速启动流程核心是使用官方脚手架egg-init$ npm install egg-init -g $ egg-init --type simple showcase cd showcase $ npm install $ npm run dev $ open http://localhost:7001其中egg-init --type simple生成一个最简 Egg 应用骨架showcase为项目名npm run dev以开发模式启动默认监听7001端口打开 http://localhost:7001 即可看到欢迎页。默认监听端口 7001 来源于 config/config.default.js 中的config.cluster.listen.port 7001同时支持通过listen.path指定 Unix Socket、通过listen.hostname指定绑定地址。开发模式下egg-development插件会自动开启文件监听与热重载。不依赖脚手架的最小示例如果不使用脚手架也可以直接利用startCluster启动一个应用// app.js const egg require(egg); egg.startCluster({ baseDir: __dirname, port: 7001, });配合app/controller、app/router.js、config/config.default.js等约定目录即可运行。仓库中的 bench/hello 就是一个极简的可运行示例含app/controller/home.js与app/router.js。约定式目录结构与加载原理Egg 的核心设计之一是“约定优于配置”Convention over Configuration。应用目录结构遵循固定约定加载器会自动扫描并装载这一逻辑完整体现在 lib/loader/app_worker_loader.js 的load()方法中load() { // app plugin core this.loadApplicationExtend(); this.loadRequestExtend(); this.loadResponseExtend(); this.loadContextExtend(); this.loadHelperExtend(); // app plugin this.loadCustomApp(); // app plugin this.loadService(); // app plugin core this.loadMiddleware(); // app this.loadController(); this.loadRouter(); // 依赖 controller }各目录的加载顺序与含义如下表加载顺序方法约定目录说明1loadApplicationExtendapp/extend/application.js扩展 Application 对象2loadRequestExtendapp/extend/request.js扩展 Request 对象3loadResponseExtendapp/extend/response.js扩展 Response 对象4loadContextExtendapp/extend/context.js扩展 Context 对象5loadHelperExtendapp/extend/helper.js扩展 Helper 工具类6loadCustomAppapp.js/app/加载应用启动钩子与自定义代码7loadServiceapp/service加载 Service业务逻辑层8loadMiddlewareapp/middleware加载中间件9loadControllerapp/controller加载 Controller依赖 Service10loadRouterapp/router.js加载路由依赖 Controller加载完成后路由会被 dump 到run/router.json见 lib/application.js 的dumpConfig()方法配置则被 dump 到run/application_config.json等文件便于排查问题。注意 config/config.default.js 中的config.dump.ignore会在 dump 时剔除password、keys、secret等敏感字段。默认配置速览config/config.default.js 是 Egg 的核心默认配置可通过app.config访问几个高频配置项值得关注配置项默认值说明keys用于 Cookie 签名/加密的密钥必须设置可用逗号分隔多个 key 轮换proxyfalse是否部署在反向代理之后为true时信任x-forwarded-*头maxIpsCount0从代理头读取的最大 IP 数量防止伪造x-forwarded-forbodyParser{ formLimit: 100kb, jsonLimit: 100kb, strict: true }请求体解析支持ignore/match黑白名单logger{ level: INFO, outputJSON: false, buffer: true }日志级别、JSON 输出、缓冲写入等httpclient{ request.timeout: 5000, httpAgent.keepAlive: true }HTTP 客户端默认超时与连接池cluster.listen.port7001默认监听端口workerStartTimeout10 * 60 * 1000Worker 启动超时超时触发startTimeout事件coreMiddleware[meta, siteFile, notfound, bodyParser, overrideMethod]核心中间件列表siteFile{ /favicon.ico: 内置图标 }站点文件映射命中即直接响应其中keys是应用启动的硬性要求在 lib/application.js 的keysgetter 中若未配置且环境为local/unittest会打印提示并要求在config/config.default.js中添加config.keys否则直接抛错。这也是新手最常见的启动错误之一。另外config.confusedConfigurations提供了“易混淆配置”的纠错能力如bodyparser→bodyParser、notFound→notfound、httpClient→httpclient检测到用户误写时会输出警告日志该逻辑在 lib/application.js 的[WARN_CONFUSED_CONFIG]方法中实现。内置进程与运行时细节Application 生命周期lib/application.js 中Application的构造函数依次完成调用父类EggApplication构造加载配置、创建 Messenger、绑定unhandledRejection监听this.loader.load()加载全部约定目录dumpConfig()导出最终配置与路由检查易混淆配置并绑定事件如cookieLimitExceed、server。handleRequest在请求进入时会发出request/response事件方便埋点统计runInBackground则用于在后台执行耗时的生成器任务。进程间通信与 Cluster 客户端lib/egg.js 中提供了app.cluster(clientClass, options)方法将cluster-client的 Leader/Follower 模式封装进 EggAgent 进程作为 LeaderApp Worker 作为 Follower公共客户端如数据库连接只在 Agent 中创建一份再通过订阅/广播同步给各 Worker从而避免多进程重复建连。相关配置项见config.clusterClient默认maxWaitTime与responseTimeout均为 60000ms。日志体系app.logger、app.coreLogger、app.getLogger(name)等接口由 lib/core/logger.js 提供通过createLoggers创建。默认日志文件位于$HOME/logs/{appname}/目录下其中appLogName默认为{appname}-web.log、coreLogName为egg-web.log、errorLogName为common-error.log、agentLogName为egg-agent.log并支持 JSON 输出与缓冲写入等生产级配置。HTTP 客户端app.curl(url, opts)是对 lib/core/httpclient.js 的封装底层基于urllib返回{ status, headers, res, data }。当config.httpclient.enableDNSCache为true时会自动切换为 lib/core/dnscache_httpclient.js 以启用 DNS 缓存。参与贡献与文档约定Egg 鼓励社区参与贡献与文档翻译README 明确说明除教程与 API 文档仍在翻译中外其余文档均为英文并欢迎加入翻译工作。贡献前请先查阅 CONTRIBUTING.md中文版见 CONTRIBUTING.zh-CN.md。如果希望深入学习仓库 docs/source 下提供了完整的英文en/与中文zh-cn/文档覆盖基础概念路由、控制器、服务、中间件、扩展、核心能力日志、安全、错误处理、HTTP 客户端、Cookie 与 Session、部署以及高级主题加载器、插件、框架定制、Cluster 客户端等主题是进一步研究 Egg 的第一手资料。小结Egg 以“框架的框架”为定位将进程管理、插件系统、框架定制与约定式加载四大能力内聚一体。通过本文你可以看到从 index.js 的入口导出到 lib/egg.js 与 lib/application.js 的继承体系再到 config/config.default.js 与 config/plugin.js 的默认配置每个特性都有清晰的源码落点可循。下一阶段建议结合 docs/source/zh-cn/ 系列文档从“使用 Egg”进阶到“理解 Egg、定制 Egg”。参考资源仓库内入口与导出index.js应用单例lib/application.jsAgent 单例lib/agent.js应用加载器lib/loader/app_worker_loader.js默认配置config/config.default.js内置插件声明config/plugin.js文档目录docs/source/en/ 与 docs/source/zh-cn/【免费下载链接】egg Born to build better enterprise frameworks and apps with Node.js Koa项目地址: https://gitcode.com/gh_mirrors/egg11/egg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考