add: OpenAPI 计费平台架构报告 + 简历技术要点
Deploy Docs / deploy (push) Successful in 48s

This commit is contained in:
2026-08-27 13:02:28 +00:00
parent f670939b1a
commit 4e5985876f
3 changed files with 408 additions and 0 deletions
@@ -0,0 +1,241 @@
# Open-API 前后端架构报告
!!! note "💡 一句话概述"
基于 Koa.js 的高性能 API 计费网关,支持按月/按量两种计费模式,采用 Redis + MongoDB 双存储架构实现实时计数与持久化。
---
## 🔑 核心概念
1. **计费网关**:RESTful API 网关,通过 `X-API-Key` 认证,支持按月固定额度和按调用量两种计费模式
2. **双存储架构**:Redis 负责高频实时计数与速率限制,MongoDB 负责持久化存储与聚合统计
3. **Koa 洋葱模型**:三层中间件链 — 错误处理 → 请求体解析 → 速率限制,层层穿透
---
## 📝 系统架构
### 分层设计
```
┌─────────────────────────────────────────────────┐
│ src/app.js (入口) │
├─────────────────────────────────────────────────┤
│ src/routes/ (路由层) │
│ ├── api.js (API 代理路由) │
│ └── billing.js (计费管理路由) │
├─────────────────────────────────────────────────┤
│ src/middleware/ (中间件层) │
│ ├── rateLimiter.js (速率限制) │
│ └── errorHandler.js (错误处理) │
├─────────────────────────────────────────────────┤
│ src/services/ (业务逻辑层) │
│ └── BillingService.js (计费服务) │
├─────────────────────────────────────────────────┤
│ src/models/ (数据模型层) │
│ ├── User.js (用户模型) │
│ └── Usage.js (使用记录模型) │
├─────────────────────────────────────────────────┤
│ src/utils/ (工具层) │
│ ├── redis.js (Redis 客户端) │
│ └── logger.js (日志工具) │
└─────────────────────────────────────────────────┘
```
### 数据流
```
┌──────────────┐ X-API-Key ┌──────────────┐
│ API 调用方 │ ──────────────→ │ Koa.js │
│ (客户端) │ ← ──────────── │ Gateway │
└──────────────┘ JSON Response └──────┬───────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ MongoDB │ │ Redis │ │ Winston │
│ (持久化) │ │ (缓存/限流) │ │ (日志) │
└───────────┘ └─────────────┘ └─────────────┘
```
---
## 🛣️ 路由设计
| 方法 | 路径 | 说明 | 认证 |
|------|------|------|------|
| `GET` | `/billing/usage/:year/:month` | 获取指定月份使用统计 | X-API-Key |
| `GET` | `/billing/bill/:year/:month` | 获取指定月份账单 | X-API-Key |
| `PUT` | `/billing/plan` | 更新计费方案 | X-API-Key |
| `*` | `/api/*` | API 代理路由(引用缺失) | — |
---
## ⚡ 中间件层
### 速率限制 (rateLimiter.js)
基于 Redis 的滑动窗口速率限制:
| 参数 | 值 | 说明 |
|------|-----|------|
| 窗口大小 | 60 秒 | `RATE_LIMIT_WINDOW` |
| 最大请求数 | 100 | `MAX_REQUESTS` |
| Redis Key | `ratelimit:{apiKey}` | 按 API Key 隔离 |
工作流程:提取 `X-API-Key` → Redis `INCR` + `EXPIRE` 原子计数 → 注入 `X-RateLimit-*` 响应头 → 超限返回 `429`。Redis 异常时降级放行。
### 错误处理 (errorHandler.js)
Koa 洋葱模型全局 try-catch,统一返回结构化错误:
```json
{
"error": {
"message": "错误描述",
"status": 500,
"timestamp": "2026-08-27T00:00:00.000Z"
}
}
```
---
## 💰 BillingService 核心逻辑
| 方法 | 功能 | 外部依赖 |
|------|------|----------|
| `recordUsage(userId, endpoint)` | 记录 API 调用使用量 | User, Usage, Redis |
| `getMonthlyUsage(userId, year, month)` | 查询月度使用统计 | Usage |
| `generateMonthlyBill(userId, year, month)` | 生成月度账单 | User, Usage |
**recordUsage 流程:**
```
查询用户 → 校验用户状态
├─ 按量计费(usage):cost = user.usageRate
└─ 按月计费(monthly):
├─ 聚合查询当月已用请求数
└─ 超出配额则抛出 'Monthly quota exceeded'
→ Redis HINCRBY 记录实时计数(7天过期)
→ MongoDB upsert 持久化使用记录
→ 按量计费时扣减用户余额
```
**计费模式对比:**
| 特性 | 按月计费 (monthly) | 按量计费 (usage) |
|------|-------------------|-----------------|
| 费用计算 | 固定月费 | 每次调用 × 费率 |
| 配额检查 | 有(月度请求上限) | 无 |
| 余额扣减 | 无 | 每次调用扣减 |
| 适用场景 | 稳定调用量 | 波动调用量 |
---
## 📊 数据模型
### User 模型
| 字段 | 类型 | 说明 |
|------|------|------|
| `username` | String | 用户名(唯一) |
| `apiKey` | String | API 密钥(唯一) |
| `billingPlan` | String | 计费模式:`monthly` / `usage` |
| `monthlyQuota` | Number | 月度配额 |
| `usageRate` | Number | 单次调用费率 |
| `balance` | Number | 账户余额,默认 0 |
| `isActive` | Boolean | 账户激活状态 |
### Usage 模型
| 字段 | 类型 | 说明 |
|------|------|------|
| `userId` | ObjectId | 关联用户 |
| `endpoint` | String | API 端点路径 |
| `requestCount` | Number | 请求次数 |
| `cost` | Number | 单次调用费用 |
| `billingPeriod.year` | Number | 计费年份 |
| `billingPeriod.month` | Number | 计费月份 |
索引:复合索引 `{ userId: 1, 'billingPeriod.year': 1, 'billingPeriod.month': 1 }`
---
## 🔧 工具层
### Redis 客户端 (utils/redis.js)
单例模式,封装 `get/set/hincrby/expire` 四个核心方法。通过 `REDIS_URL` 环境变量配置。
### 日志工具 (utils/logger.js)
Winston 框架,生产环境 `info` 级别,开发环境 `debug` 级别。输出至 `error.log`、`combined.log` 和控制台。
---
## 📦 核心依赖
| 依赖 | 版本 | 用途 |
|------|------|------|
| `koa` | ^2.14.2 | HTTP 框架 |
| `koa-router` | ^12.0.1 | 路由管理 |
| `mongoose` | ^7.5.0 | MongoDB ODM |
| `redis` | ^4.6.8 | Redis 客户端 |
| `winston` | ^3.10.0 | 日志框架 |
| `joi` | ^17.10.1 | 数据验证 |
| `dotenv` | ^16.3.1 | 环境变量加载 |
---
## 🏗️ 性能优化策略
| 策略 | 实现方式 | 效果 |
|------|----------|------|
| Redis 速率限制 | 原子 `INCR` + `EXPIRE` | 毫秒级限流判断 |
| Redis 使用量缓存 | `HINCRBY` 实时计数 | 避免高频 MongoDB 写入 |
| MongoDB 聚合索引 | 复合索引 `{userId, year, month}` | 加速月度统计查询 |
| 异步持久化 | Redis 先写 → MongoDB 后写 | 降低 API 响应延迟 |
| 连接池复用 | Mongoose/Redis 连接单例 | 降低连接开销 |
---
## ⚠️ 已知问题
!!! warning "待修复项"
- `src/routes/api.js` 缺失 — `app.js` 引用但文件不存在
- `src/middleware/auth.js` 缺失 — `billing.js` 引用 `validateApiKey` 但文件未定义
- User 模型未导入 — `billing.js:32` 使用 `User.findByIdAndUpdate` 但未 import
- mongoose 未导入 — `BillingService.js:89` 使用 `mongoose.Types.ObjectId` 但未 import
---
## 🗂️ 项目文件清单
```
Open-API/
├── README.md
├── package.json
└── src/
├── app.js # 应用入口
├── models/
│ ├── User.js # 用户数据模型
│ └── Usage.js # 使用记录数据模型
├── services/
│ └── BillingService.js # 计费业务逻辑
├── routes/
│ └── billing.js # 计费管理路由
├── middleware/
│ ├── rateLimiter.js # 速率限制中间件
│ └── errorHandler.js # 错误处理中间件
└── utils/
├── redis.js # Redis 客户端封装
└── logger.js # Winston 日志配置
```
---
## 🔗 相关链接
- [Open-API GitHub 仓库](https://github.com/kainy/Open-API) — 项目源码
- [简历技术要点](openapi-billing-resume.md) — 基于源码提炼的面试技术要点
+164
View File
@@ -0,0 +1,164 @@
# Open-API 简历技术要点
!!! note "💡 一句话概述"
基于项目源码实际实现提炼的 8 个核心技术要点,每个均可深入到函数级别展开,适合简历项目描述和面试准备。
---
## 🔑 核心概念
1. **高性能 API 计费网关**:Koa.js + Redis + MongoDB 双存储架构,支撑按月/按量两种计费模式
2. **分布式速率限制**:Redis 滑动窗口算法 + 事务保证原子性,优雅降级策略保障可用性
3. **数据一致性设计**:Redis 先写 → MongoDB 后写策略,在响应速度与数据可靠性间取得平衡
---
## 📝 技术要点详解
### 1. 高性能 API 计费网关设计
基于 Koa.js 构建 RESTful API 计费网关,支持按月固定额度和按调用量两种计费模式。
**Redis + MongoDB 双存储架构:**
- **Redis**:通过 `HINCRBY` 原子操作实现实时使用量计数(高频写入场景)
- **MongoDB**:通过 `$match` + `$group` 聚合管道进行月度统计与账单生成(低频读取场景)
- **幂等写入**:使用 `findOneAndUpdate` upsert 操作保证使用记录的幂等性
- **索引优化**:复合索引 `{userId, billingPeriod.year, billingPeriod.month}` 优化聚合查询
- **缓存策略**:Redis Key 设置 7 天 TTL 自动过期,平衡内存与数据时效性
---
### 2. 基于 Redis 的分布式速率限制
采用滑动窗口算法实现 API 级别速率限制。
**实现细节:**
- Redis `INCR` 原子递增 + `EXPIRE` 设置 60 秒窗口
- 单 API Key 最大 100 次请求/分钟
- Redis `MULTI` 事务保证计数与过期设置的原子性
- 响应 Header 注入 `X-RateLimit-Limit` 和 `X-RateLimit-Remaining`
**降级策略:** Redis 连接异常时自动放行请求(`await next()`),避免缓存层故障导致服务不可用。超限返回 `429 Too Many Requests`,与标准 HTTP 语义对齐。
---
### 3. 多层中间件链与全局错误处理
基于 Koa 洋葱模型设计三层中间件链:
```
errorHandler(全局异常捕获)→ bodyParser(JSON 解析)→ rateLimiter(速率限制 + 认证)
```
**错误处理机制:**
- try-catch 包裹整个请求生命周期,统一捕获同步/异步异常
- 返回结构化错误响应 `{message, status, timestamp}`,避免堆栈信息泄露
- `ctx.app.emit('error', err, ctx)` 触发应用级错误事件,为监控告警提供扩展点
- 速率限制中间件在 Redis 异常时降级放行,保证系统可用性优先
---
### 4. 计费服务核心逻辑与配额管理
`BillingService` 类封装计费核心逻辑,通过静态方法实现无状态服务设计。
**按月计费模式:**
- MongoDB 聚合管道(`$match` + `$group`)实时计算当月已用请求数
- 超出 `monthlyQuota` 配额时抛出异常拦截后续调用
**按量计费模式:**
- 每次调用按 `usageRate` 费率扣减用户 `balance` 字段
- `$inc` 原子操作保证并发安全
**账单生成:** 按月取固定费用,按量按端点维度聚合 `requestCount × cost`。
**数据一致性:** 所有写操作遵循"Redis 先写 → MongoDB 后写"策略,保证响应速度的同时确保数据最终一致性。
---
### 5. Redis 单例客户端封装
实现 `RedisClient` 静态工具类,采用懒加载单例模式管理 Redis 连接。
**设计要点:**
- 首次调用 `connect()` 时创建连接并缓存至静态属性 `client`,后续直接复用
- 封装 `get/set/hincrby/expire` 四个核心方法,屏蔽底层 API 差异
- `set` 方法支持可选 `expireSeconds` 参数,内部自动选择 `setEx` 或 `set`
- 连接地址通过 `REDIS_URL` 环境变量配置,支持不同环境无缝切换
- 连接生命周期事件(`error`/`connect`)接入 Winston 日志
---
### 6. Winston 多通道日志系统
基于 Winston 构建分级日志系统:
| 配置 | 生产环境 | 开发环境 |
|------|---------|---------|
| 日志级别 | `info` | `debug` |
| 格式 | JSON + 时间戳 | JSON + 时间戳 |
| error.log | ✅ 仅错误级别 | ✅ 仅错误级别 |
| combined.log | ✅ 全部级别 | ✅ 全部级别 |
| 控制台输出 | ❌ | ✅ 带颜色高亮 |
通过 `NODE_ENV` 环境变量自动切换日志策略,JSON 格式便于 ELK/日志平台采集解析。
---
### 7. MongoDB 数据模型与索引优化
**User 模型:**
- `apiKey` 唯一索引支撑 API Key 认证
- `billingPlan` 枚举字段区分计费模式
**Usage 模型:**
- `billingPeriod` 嵌入式文档实现时间分区
- 复合索引 `{userId, year, month}` 支撑高效聚合查询
- `mongoose.Schema.Types.ObjectId` + `ref` 建立用户-使用记录关联,支持 `$lookup` 联表
- `requestCount` 字段配合 `$inc` 原子递增,避免并发计数丢失
---
### 8. 环境变量驱动的配置管理
**安全隔离策略:**
- `dotenv` 实现环境变量集中管理,敏感配置不硬编码于源码
- `.env` 文件隔离不同环境配置,配合 `.gitignore` 防止敏感信息泄露
- API 密钥通过 `X-API-Key` header 传递,避免 URL 参数暴露风险
- 错误响应仅返回 `message` 和 `status`,不泄露内部堆栈或数据库查询细节
**核心配置项:**
| 配置项 | 说明 |
|--------|------|
| `PORT` | 服务端口,默认 3000 |
| `MONGODB_URI` | MongoDB 连接地址 |
| `REDIS_URL` | Redis 连接地址 |
| `NODE_ENV` | 运行环境 |
---
## ⚠️ 面试展开建议
!!! warning "准备要点"
- **计费模式**:能画出 `recordUsage` 的完整流程图,解释按月/按量的分支逻辑
- **Redis vs MongoDB**:为什么高频写入用 Redis、统计聚合用 MongoDB?各自的优势场景
- **速率限制降级**:Redis 挂了怎么办?为什么选择降级放行而不是阻断请求?
- **数据一致性**:Redis 先写 MongoDB 后写,如果 MongoDB 写入失败怎么处理?
- **原子操作**:`HINCRBY`、`$inc`、`MULTI` 分别在什么场景下使用?
---
## 🔗 相关链接
- [Open-API GitHub 仓库](https://github.com/kainy/Open-API) — 项目源码
- [前后端架构报告](openapi-billing-architecture.md) — 完整架构分析
+3
View File
@@ -86,6 +86,9 @@ nav:
- 首页: index.md
- 项目:
- project/index.md
- Open-API 计费平台:
- project/openapi-billing-architecture.md
- 简历技术要点: project/openapi-billing-resume.md
- 架构:
- architecture/index.md
- 缓存: