多进程模型和进程间通讯

1. 背景与问题

  • Node.js 单线程特性导致单个进程只能利用 1 个 CPU 核心,无法发挥多核服务器的性能优势。

  • 官方通过 Cluster 模块实现多进程,Egg.js 在此基础上扩展为更健壮的企业级多进程模型。

2. 核心进程角色

进程类型

数量

核心作用

稳定性

业务代码

Master

1

进程管理、消息转发、异常重启

极高

 不运行

Agent

1

后台公共任务(如日志归档、长连接)

 少量

Worker

通常为 CPU 核数

处理用户请求、执行业务代码

一般

 运行

3. 关键机制

  • 进程守护:Worker 异常退出时,Master 会立即重启新 Worker;Agent 异常时仅记录日志等待人工处理,避免频繁重启影响公共服务。

  • 启动时序:Master → Agent → Worker,所有进程就绪后触发 egg-ready 事件,此时才能安全使用 IPC 通讯。

  • IPC 通讯:通过 messenger 对象封装,仅支持 Master ↔ Worker/Agent 直接通讯,Worker 间需 Master 转发。

常用 API:

// 发送消息
app.messenger.sendToAgent('event', data);
app.messenger.sendToApp('event', data);
app.messenger.broadcast('event', data);

// 接收消息
app.messenger.on('event', (data) => { /* 处理逻辑 */ });

4. Agent 机制

框架的启动时序如下:

  1. Master 启动后先 fork Agent 进程。
  2. Agent 初始化成功后,通过 IPC 通道通知 Master。
  3. Master 接着 fork 多个 App Worker。
  4. App Worker 初始化成功后,通知 Master。
  5. 所有进程初始化成功后,Master 通知 Agent 和 Worker,应用启动成功。
// agent.js
module.exports = agent => {
  // 在这里写你的初始化逻辑。

  // 你还可以通过 messenger 对象发送消息给 App Worker。
  // 但是,需要等 App Worker 启动成功后才能发送,否则可能丢失消息。
  agent.messenger.on('egg-ready', () => {
    const data = { ... };
    agent.messenger.sendToApp('xxx_action', data);
  });
};
// app.js
module.exports = (app) => {
  app.messenger.on('xxx_action', (data) => {
    // ...
  });
};

5. 实战场景:缓存更新

结合定时任务 + IPC 实现三种缓存更新策略:

  1. 兜底更新:所有 Worker 每 10 分钟拉取最新数据。

  2. 主动检查:单个 Worker 每 10 秒检查数据源变化,通过 IPC 通知所有 Worker 更新。

  3. 被动推送:Agent 监听消息中间件,收到变更后广播给所有 Worker。
    实战demo:test-egg/eggdemo07 at main · EggFlower10/test-egg


二、国际化(I18n)

1. 基础配置

  • 默认语言:en-US,可通过配置修改为简体中文:

    // config/config.default.js
    exports.i18n = {
      defaultLocale: 'zh-CN',
    };
    • 语言切换优先级:query > cookie > header,支持自定义参数名和 Cookie 过期时间

    2. 多语言文件

    • 存放路径:config/locale/*.js*.json,框架会自动合并应用、框架、插件的多语言配置。

    • 示例(zh-CN.js):

      module.exports = {
        'Email': '邮箱',
        'Welcome back, %s!': '欢迎回来,%s!',
      };

      3. 多语言文本使用

      • 在 Controller 中通过 ctx.__()(或别名 ctx.gettext())获取:

        ctx.__('Welcome back, %s!', ctx.user.name);

        • 在 View(如 Nunjucks)中直接使用:

          <li>{{ __('Email') }}:{{ user.email }}</li>
          • 支持 %s 占位符和数组下标占位符(如 {0}{1})。


          三、View 模板渲染

          1. 核心设计

          • 框架内置 @eggjs/view 作为基础层,支持多模板引擎以插件形式引入(如 @eggjs/view-nunjucks),保持渲染 API 统一。

          2. 快速上手(以 Nunjucks 为例)

          1. 安装插件

            npm i @eggjs/view-nunjucks
            1. 启用插件

              // config/plugin.js
              exports.nunjucks = {
                enable: true,
                package: 'egg-view-nunjucks',
              };

              1. 配置插件

                // config/config.default.js
                exports.view = {
                  root: 'app/view', // 模板根目录
                  defaultViewEngine: 'nunjucks', // 全局默认模板引擎
                  defaultExtension: '.nj', // 默认文件后缀
                  mapping: { '.nj': 'nunjucks' }, // 后缀与引擎映射
                };

                3. 渲染接口

                框架在 Context 上提供三个渲染接口:

                • ctx.render(name, locals):渲染模板并赋值给 ctx.body

                • ctx.renderView(name, locals):仅渲染模板,不赋值。

                • ctx.renderString(tpl, locals, options):渲染模板字符串。

                示例:

                await ctx.render('home.nj', { name: 'Egg.js' });
                ctx.body = await ctx.renderString('Hi, {{ name }}', { name: 'Egg.js' });

                4. 本地变量(Locals)

                • app.locals:全局变量,在 app.js 中配置,所有请求共享。

                • ctx.locals:请求级变量,会自动合并 app.locals,且仅首次访问时合并。

                • 框架会自动将 render 传入的 data 合并到 ctx.locals,并注入 ctxrequesthelper 等对象。

                5. 辅助与安全

                • Helper:在 app/extend/helper.js 中扩展方法,模板中直接调用(如 helper.lowercaseFirst(str))。

                • 安全插件:内置 @eggjs/security,提供 helper.shtmlsurl 等安全转义函数,避免 XSS 风险。

                实战demo:EggFlower10/test-egg: 这是一个学习egg.js的仓库

                Logo

                有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

                更多推荐