里程碑1 基于nodejs实现服务端内核引擎

6 阅读1分钟

一、里程碑目标

系统目标:

开发一个支持多网站建设的系统平台或应用框架,通过配置化沉淀80%的重复性工作,并提供定制化能力支持20%的定制化需求

里程碑1目标:

实现系统的BFF(Backend for Frontend)层,实现了一个基于nodejs的应用框架,采用 Koa 实现,通过 Loader 实现模块化、可扩展的架构,实现一个后端 API 服务 + 服务端渲染的应用框架

二、BFF层架构

1.接入层:包括路由定义、参数校验规则、中间件等。 

2.业务层:包括各种controller,处理具体的业务逻辑,调用服务层的service。 

3.服务层:由各个service组成,每个service包含多种原子性方法,针对数据层的原子性操作

三、BFF的优势

1.解耦展示层和后端接口:后端只需关注接口业务逻辑,前端在BFF层完成展示逻辑。 

2.接口请求收拢:所有对外请求由BFF层处理,减少内网到外网的请求,利用服务对服务的处理,在同一个集群速度会更快,提升系统性能。 

3.签名和密钥管理:BFF层管理后端签名和密钥,保护后端API服务器。 

4.SSR能力:BFF层具备SSR能力,可以实现前端业务的更多可能性。

四、loader机制

1、loader设计模式

核心是 Loader 系统 ,采用统一的加载模式:

// 每个 loader 都是 (app) => {} 函数
module.exports = (app) => {
  // 1. 读取目录文件
  const fileList = glob.sync(path.resolve(path, '**/*.js'));
  
  // 2. 遍历文件,解析模块名(支持多级目录)
  //    custom-module/custom-controller.js => customModule.customController
  
  // 3. 实例化模块并挂载到 app 上
  app.controller = { customModule: { customController: instance } };
};

2、核心loader

configLoader - 配置管理

功能 :根据环境加载不同配置文件

// 加载顺序:defaultConfig → envConfig(覆盖)
app.config = { ...defaultConfig, ...envConfig };
  • 支持三种环境:local/beta/production 

  • 通过 process.env._ENV 控制环境 

  • 默认配置 + 环境配置的继承覆盖模式

extendLoader - Koa 实例扩展

功能 :向 Koa 实例添加自定义属性/方法

示例:日志扩展

module.exports = (app) => {
  let logger;
  if (app.env.isLocal()) {
    logger = console;  // 本地使用 console
  } else {
    log4js.configure({...});  // 生产环境使用 log4js
    logger = log4js.getLogger();
  }
  return logger;
};

middlewareLoader - 中间件加载器

功能 :加载 app/middleware/ 下的所有中间件到 app.middlewares

  • 支持多级目录结构

  • 自动将 kebab-case 转换为 camelCase

  • 示例: api-sign-verify.js → app.middlewares.apiSignVerify

controllerLoader & serviceLoader - 业务模块加载器

功能:加载 app/controller/ 下的所有 controller 到app.controller,加载app/service/ 下的所有 service到app.service

Controller 依赖 Service

routerSchemaLoader - API 参数校验

功能:将 JSON Schema 与路由绑定

// router-schema/project.js
module.exports = {
  '/api/project/list': {
    get: {
      query: { type: 'object', properties: {...}, required: ['proj_key'] },
      body: {},
      params: {}
    }
  }
};

JSON Schema介绍

  • JSON Schema用于描述JSON数据的结构,确保数据符合预期的格式和规则。
  • 通过JSON Schema,可以定义JSON对象中的属性、类型、是否必传等规则

校验流程:

请求 → api-params-verify 中间件 → 读取 app.routerSchema[path][method] → 使用 Ajv 校验参数 → 校验失败返回 400 错误

routerLoader - 路由注册

功能 :注册所有路由到 Koa

  • 支持多级目录路由

  • 路由兜底处理(匹配失败重定向到首页)

五、中间件系统

1、中间件执行顺序

// middleware.js
app.use(koaStatic(...));              // 静态资源
app.use(koaNunjucks(...));            // 模板渲染
app.use(bodyparser());                // 请求体解析
app.use(errorHandler);                // 错误处理(兜底)
app.use(apiSignVerify);               // 签名验证
app.use(apiParamsVerify);             // 参数校验

2、签名验证中间件

签名 = md5(signKey + '_' + timestamp)

API签名校验

  • 签名的时效性:确保API请求有时效性,防止被重复利用
  • 签名校验中间件:API.verify,用于验证API请求的签名合法性
  • 签名的生成与验证:使用MD5算法,通过密钥和时效性生成签名,并验证签名的正确性

前端签名生成

  • 前端签名生成:在前端生成签名和时效性,并将其作为请求头发送到后端
  • 签名验证逻辑:后端使用相同的密钥和时效性验证签名,确保请求合法

3、错误处理中间件

兜底策略 :

try {
  await next();
} catch (error) {
  // 模板未找到 → 重定向
  if (error.message.indexOf('template not found') > -1) {
    ctx.redirect(app.options.homePage);
    return;
  }
  
  // 其他错误 → 返回统一格式
  ctx.status = 200;
  ctx.body = {
    success: false,
    code: 50000,
    message: '网络异常,请稍后重试'
  };
}
  • .中间件的作用:处理洋葱圈模型中的错误,确保错误被捕获并处理

  • 通过try catch捕获后续中间件和业务逻辑中的异常,统一处理错误

  • 异常处理逻辑:记录错误信息、生成错误码,并返回给前端

六、日志扩展

1.日志落地的重要性:日志需要写在文件上,以便在出现问题时可以重新查看和分析。 

2.log4js工具:用于在service层输出日志,确保日志落地。 

3.日志的时效性:日志应记录具体的时间点和动作,以便定位问题。

4.日志级别的定义:包括info、warn、error等级别,用于区分日志的重要性。 

5.日志的输出环境:本地开发环境下打印在控制台,测试和生产环境下输出并落地到磁盘。

七、启动顺序

// 1. 环境配置
app.env = env();

// 2. 配置加载器(尽早加载,供其他模块使用)
configLoader(app);

// 3. 扩展加载器(扩展可能用到配置)
extendLoader(app);

// 4. 中间件加载器(中间件加载器本身不依赖,但中间件可能依赖)
middlewareLoader(app);

// 5. 路由 Schema 加载器
routerSchemaLoader(app);

// 6. Service 加载器(Service 用到 app.config, app.extend)
serviceLoader(app);

// 7. Controller 加载器(Controller 用到 app.config, app.extend, app.service)
controllerLoader(app);

// 8. 全局中间件(全局中间件用到所有已加载的模块)
require(middleware.js)(app);

// 9. 路由加载器(路由用到 controller, service)
routerLoader(app);