dracokite
v1.0.3
Published
dracokite - A multi-process Node.js backend framework with built-in BFF capabilities
Downloads
643
Maintainers
Readme
dracokite 框架实现规格书
1. 框架定位
dracokite 是一个自带 BFF 能力的多进程 Node.js 后端框架。
- 包名:
dracokite - 语言:TypeScript(严格模式),同时兼容 JavaScript
- 模块:ESM
- 依赖:仅 Node.js 内置模块,零第三方依赖
- 核心理念:文件即路由,BFF 即方法,多进程即默认
2. 架构全景
2.1 进程编制(强制多进程,最低 7 进程)
text
dracokite ×1 ← 唯一大脑,掌控全局
├── RequestingProcess ×1 ← 管流量,执行 dracokite 调度
│ ├── SyncWorker ×N ← 同步通道,处理快速请求
│ └── AsyncWorker ×N ← 异步通道,处理长任务/WebSocket
└── InformationProcess ×1 ← 管后勤,执行 dracokite 调度
├── CronProcess ×1 ← 定时任务
└── LoggerProcess ×1 ← 日志收集dracokite:最高领导,监控、决策、调度RequestingProcess:HTTP 入口,管理同步/异步双队列InformationProcess:管理定时任务和日志SyncWorker:同步 Worker,快速返回AsyncWorker:异步 Worker,处理长连接、文件、流式响应CronProcess:执行定时任务LoggerProcess:收集和写入日志
2.2 每个进程最少 3 个线程
| 进程 | 线程数 | 分工 | | :----------------- | :----- | :---------------------------------------------- | | dracokite | 3 | 主线程(决策)+ 状态收集线程 + 指令下发线程 | | RequestingProcess | 3 | 主线程(事件循环)+ 路由匹配线程 + 队列管理线程 | | SyncWorker | 3 | 主线程(处理请求)+ 辅助线程 + 预留线程 | | AsyncWorker | 3 | 主线程(事件循环)+ 线程池(至少1线程) | | InformationProcess | 3 | 主线程(调度)+ 定时任务触发线程 + 状态收集线程 | | CronProcess | 3 | 主线程(调度器)+ 2 个任务执行线程 | | LoggerProcess | 3 | 收线程 + 写线程 + 缓冲管理线程 |
最低 7 进程 × 3 线程 = 21 个执行单元。
2.3 通信规则
- 树状单向通信,严禁网状
- dracokite ↔ RequestingProcess:独立 IPC 通道,最高优先级
- dracokite ↔ InformationProcess:独立 IPC 通道
- RequestingProcess → Worker:Worker 主动来队列领取任务
- 所有进程 → LoggerProcess:单向扔日志,不等回执
- 心跳:所有下级定期向上级 dracokite 上报
3. 路由系统
3.1 文件系统路由
pages/目录结构即 URL 路径[param].js表示动态参数- 文件名即路径,函数名即 HTTP 方法
示例:
| 文件 | URL | 方法 |
| :--------------------- | :----------- | :-------------- |
| pages/users/[id].js | /users/:id | get() → GET |
| pages/users/index.js | /users | post() → POST |
3.2 类继承 Router
每个路由文件必须 export default class extends Router。
Router 基类提供:
this.request:params、query、body、headers、files、method、pathname、ipthis.response:status、body、headers、cookies、sessionthis.state:钩子间共享数据this.requestId:全局唯一 ID
静态属性:
static dataType:定义入参static timeout:路由级超时时间(默认 30000ms)static channel:'sync'或'async',不写默认sync
3.3 dataType 定义规则
仅定义 params、query、body,不包含 headers。
javascript
static dataType = {
get: {
params: { id: 'string' },
query: { page: 'number' }
},
post: {
body: { name: 'string', age: 'number' }
}
}- 定义了就是必填,客户端缺字段返回 400
- 多传字段不报错
- 支持的类型:
'string'、'number'、'int'、'boolean'、'email'、'url'、'uuid'、'phone'、'date'、'datetime'、'file'、'image'、'base64'、'buffer'、'stream'、'json'、'xml'、'csv' - 嵌套对象:
{ city: 'string', zip: 'number' } - 数组:
['string']、[{ name: 'string' }] - 文件类型:
'file'、'file[]'、'image'、'image[]'
3.4 路由匹配优先级
固定路由优先于动态路由。例如 pages/users/profile.js 优先于 pages/users/[id].js。启动时检测冲突路径并警告。
4. 请求与响应
4.1 自动解析
- JSON body、form-urlencoded、multipart/form-data 自动解析
- 文件放入
this.request.files - 默认请求体上限 10MB(非文件上传),通过
bodyMaxSize配置,单位 MB - 文件上传大小限制通过
upload.maxSize配置
4.2 返回值风格
业务方法直接 return 数据。
- 返回对象/数组 → 自动 JSON
- 返回字符串 →
text/plain - 返回数字 → HTTP 状态码
- 返回
null→ 204 - 不返回 → 默认 200
特殊返回字段(仅在 return 对象中):
__code__:业务码,直接返回,不走错误码文件__header__:设置响应头__cookie__:设置 Cookie__session__:写入 Session
4.3 resError 方法
this.resError(code, options?)- 走错误码文件
errors.json查找 message options.isPenetrate:若true,code同时作为 HTTP 状态码;否则 HTTP 200- 支持多语言,根据请求头
Accept-Language自动选择
4.4 错误码文件
json
{
"zh": { "10001": "用户名已存在" },
"en": { "10001": "Username already exists" }
}在 App 构造函数中通过 errors 指定路径。
4.5 自定义错误格式
javascript
const app = new App({
errorFormat: (code, message, requestId) => ({
code,
message,
requestId,
timestamp: Date.now()
})
})5. 钩子系统
5.1 全局钩子(在 app.js 注册)
| 钩子 | 触发时机 |
| :----------------------- | :------------------------------ |
| beforeRequest(router) | 请求到达,尚未匹配路由 |
| afterRequest(router) | 业务方法执行后,响应前 |
| beforeResponse(router) | afterRequest 之后,序列化之前 |
| afterResponse(router) | 响应已发送后 |
5.2 路由级钩子(在 Router 子类中重写)
| 钩子 | 触发时机 |
| :----------------- | :--------------------------------- |
| beforeExecute() | 业务方法执行前 |
| afterExecute() | 业务方法执行后 |
| beforeResponse() | 全局 afterRequest 之后,序列化前 |
| afterResponse() | 全局 afterResponse 之后 |
| onError(error) | 任何钩子或业务方法抛出错误 |
| onTimeout() | 请求超时 |
执行顺序:
text
全局 beforeRequest → 路由 beforeExecute → 业务方法 → 路由 afterExecute →
路由 beforeResponse → 全局 afterRequest → 全局 beforeResponse →
路由 afterResponse → 全局 afterResponse6. BFF 能力
6.1 this.bff 方法
- 基本用法:
this.bff({ user: fetch(...), orders: fetch(...) }) - 数组用法:
this.bff([fetch(...), fetch(...)]) - 超时:全局
timeout,单接口timeoutEach - 降级:
fallback对象 - 串行依赖:
this.bff.step(name, promise) - 条件请求:动态构建并发对象
- 多端裁剪:
this.is('web'|'mobile'|'desktop')
6.2 其他 BFF 能力
- 重试:
this.bff.retry(promise, { times, delay }) - 缓存:
this.bff.cache(key, promise, { ttl }) - 合并重复请求:
this.bff.dedupe(key, promise, { window }) - 日志追踪:
trace: true
7. 双队列机制
RequestingProcess 管理两个队列:
- 同步通道队列:处理快速请求,由 SyncWorker 领取
- 异步通道队列:处理长连接、文件上传、流式响应,由 AsyncWorker 领取
分流逻辑:
- WebSocket 升级请求 → 异步
- 文件上传(multipart)→ 异步
- 路由标记
static channel = 'async'→ 异步 - 其他 → 同步
AsyncWorker 空闲时可帮同步通道,反之不行。
SyncWorker 强制超时 30 秒,超时自动中断并触发 onTimeout。
8. 定时任务
tasks/ 目录下文件,继承 Task,用 static schedule 定义执行时间。
javascript
export default class extends Task {
static schedule = '0 3 * * *' // cron 表达式
async execute() {
// 业务逻辑
}
}支持简写:@daily、@hourly、@every 5m、@every 30s。
CronProcess 独立进程执行,每个任务在独立线程跑,互不影响。
9. 日志系统
框架自动采集,用户只需注册回调:
javascript
app.accessLog((info) => {
// info: { method, path, status, duration, requestId, ip, ... }
})LoggerProcess 收线程 + 写线程 + 缓冲线程,写盘慢不阻塞收。
10. 其他内置功能
| 功能 | 说明 |
| :--------- | :---------------------------------------------- |
| 静态文件 | public/ 目录自动映射,缓存策略 max-age=3600 |
| 压缩 | 默认 brotli level 6,阈值 1KB |
| 上传 | this.file.save(),分片 appendChunk/merge |
| 限流 | app.setLimit({ max, window }) 返回函数 |
| 健康检查 | app.healthCheck({...}),/health 自动暴露 |
| 监控指标 | /metrics Prometheus 格式 |
| CORS | 导出 cors 函数,放 beforeRequest 使用 |
| Session | this.session,配置存储驱动 |
| 队列 | this.queue.push() |
| 多环境配置 | config/ 目录,按 NODE_ENV 合并 |
| 数据库 | this.db 连接池 |
| 优雅关闭 | SIGTERM 按序退出,退回未完成请求 |
| CPU 绑核 | affinity: true |
| 热重载 | 开发模式监听整个项目,部分重载 |
| Debug | __routes 路由表,时间线,错误栈 |
| 启动进度 | 清屏显示当前步骤,成功后打印最终状态 |
11. 配置校验
new App({...}) 必须校验配置字段,拼错字段启动时报错并提示最接近的正确字段名。
12. Worker 预热
Worker 启动后自动执行一轮空请求预热,JIT 编译完成后再标记为就绪,RequestingProcess 只向就绪 Worker 分发任务。
13. 框架入口
用户侧代码:
javascript
import { App, Router, Task } from 'dracokite'
const app = new App({
port: 3000,
errors: './errors.json'
})
app.beforeRequest(authHook)
app.accessLog(logHandler)
app.healthCheck({ db: checkDb })
app.start()导出:App、Router、Task。
14. 实现要求
- TypeScript 严格模式
- 零第三方依赖,仅用 Node.js 内置模块
- 单文件
core.ts或合理拆分 - ESM 模块
- 所有公共接口完整类型导出
- 多进程对用户透明,开发体验与单进程一致
