Files
Edu/docs/modules/api-gateway/README.md
SpecialX 2ba4250165
Some checks failed
CI Go / test (push) Has been cancelled
CI Proto / lint (push) Has been cancelled
CI Python / test (push) Has been cancelled
CI TypeScript / test (push) Has been cancelled
feat(p1): complete P1 foundation stage
- monorepo: pnpm workspace + go.work + pyproject.toml + commitlint/husky
- infra: docker-compose (minimal + full profiles) + init-sql + prometheus
- arch-scan: multi-language scanner skeleton (TS/Go/Python/Proto)
- shared-proto: buf v2 + classes.proto (ClassService CRUD contract)
- api-gateway: Go/Gin + JWT HS256 auth + reverse proxy + request ID
- classes: NestJS golden template (error system + observability + middleware + CRUD + tests)
- teacher-portal: Next.js + paper-feel UI design system
- CI/CD: 4 workflows (go/ts/py/proto)
- docs: migration guide + project_rules + coding-standards + git-workflow + ui-design-system + 004 + 9 module READMEs + known-issues + spec/plan migration + roadmap
2026-07-07 23:39:37 +08:00

282 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# api-gateway 网关服务
> 版本1.0
> 日期2026-07-07
> 状态P1 已交付
> 关联文档:[架构影响地图](../../architecture/004_architecture_impact_map.md)
---
## 1. 模块职责
api-gateway 是整个 Edu 平台的统一入口网关,承担以下核心职责:
- **路由转发**:将外部 HTTP/REST 请求按路径前缀转发到对应 BFF 或业务服务
- **JWT 鉴权**:使用 RS256 公钥校验 JWT 签名,提取 userId/role/dataScope 注入下游 metadata
- **限流熔断**:基于 Redis 令牌桶限流,基于错误率的滑动窗口熔断
- **CORS 跨域**:处理浏览器跨域预检请求
- **访问日志**结构化访问日志zap含 traceId/userId/latency/statusCode
- **健康检查**`/healthz``/readyz` 端点供 Kubernetes 探针使用
**不做的事**:不写业务逻辑、不访问数据库、不持有业务状态。
---
## 2. 技术栈
| 类别 | 技术 | 版本 | 用途 |
|------|------|------|------|
| 语言 | Go | 1.22+ | 高性能网关 |
| Web 框架 | Gin | 1.10+ | HTTP 路由与中间件链 |
| JWT | golang-jwt/v5 | 5.2+ | RS256 签名校验 |
| 缓存/限流 | Redis | 7.x | 令牌桶计数、会话缓存 |
| 配置 | viper | 1.18+ | 多源配置env/yaml/configmap |
| 日志 | zap | 1.27+ | 结构化高性能日志 |
| 链路追踪 | OpenTelemetry | 1.24+ | 分布式追踪上下文注入 |
| 指标 | prometheus/client_golang | 1.19+ | RED 指标暴露 |
| 容器化 | Docker | - | 多阶段构建 |
| 编排 | Kubernetes | 1.28+ | 生产部署 |
---
## 3. 架构图
```mermaid
graph TB
subgraph Client["客户端"]
MFE[微前端 4 端]
end
subgraph Gateway["api-gateway"]
subgraph Middleware["中间件链"]
CORS[CORS 中间件]
LOG[访问日志 zap]
TRACE[OpenTelemetry trace]
AUTH[JWT RS256 鉴权]
RATE[限流 Redis 令牌桶]
CB[熔断器 滑动窗口]
end
ROUTER[Gin Router]
PROXY[gRPC 反向代理]
end
subgraph Downstream["下游服务"]
TBFF[teacher-bff]
SBFF[student-bff]
PBFF[parent-bff]
IAM[iam 登录特殊路由]
end
subgraph Infra["基础设施"]
REDIS[(Redis<br/>限流计数)]
JWKSIAM[(IAM JWKS 公钥)]
OTEL[OTLP Collector]
end
MFE -->|HTTP/REST| CORS
CORS --> LOG --> TRACE --> AUTH --> RATE --> CB --> ROUTER
ROUTER --> PROXY
PROXY -->|gRPC| TBFF
PROXY -->|gRPC| SBFF
PROXY -->|gRPC| PBFF
ROUTER -.特殊路由.-> IAM
RATE --> REDIS
AUTH --> JWKSIAM
LOG --> OTEL
TRACE --> OTEL
```
---
## 4. 核心流程图
### 4.1 鉴权与路由流程
```mermaid
sequenceDiagram
participant U as 微前端
participant GW as API Gateway
participant R as Redis
participant SVC as 下游服务
U->>GW: GET /api/classes + Bearer token
GW->>GW: CORS 校验
GW->>GW: 记录访问日志traceId 生成)
GW->>GW: OpenTelemetry span 创建
GW->>GW: 提取 Authorization header
GW->>GW: RS256 公钥校验签名
GW->>GW: 校验 exp/iss/aud
GW->>GW: 提取 userId/role/dataScope
GW->>R: 令牌桶限流检查userId 维度)
R-->>GW: 通过
GW->>GW: 熔断器状态检查
GW->>SVC: gRPC 请求 + metadata 透传
SVC-->>GW: gRPC 响应
GW-->>U: HTTP 响应 + traceId header
```
### 4.2 限流流程
```mermaid
flowchart TD
REQ[请求到达] --> Q1{Redis 令牌桶<br/>剩余令牌 >= 1?}
Q1 -- 是 --> DEC[扣减令牌<br/>INCR + EXPIRE]
DEC --> PASS[放行]
Q1 -- 否 --> Q2{是否超量请求?}
Q2 -- 是 --> REJ1[429 Too Many Requests<br/>Retry-After header]
Q2 -- 否 --> WAIT[排队等待<br/>短超时]
WAIT --> Q1
```
### 4.3 登录特殊处理流程
```mermaid
sequenceDiagram
participant U as 用户
participant GW as API Gateway
participant IAM as IAM 服务
Note over GW: /auth/* 路径跳过 JWT 鉴权
U->>GW: POST /auth/login {username, password}
GW->>GW: 限流检查IP 维度)
GW->>IAM: gRPC Login(username, password)
IAM->>IAM: 校验密码
IAM->>IAM: 生成 JWTRS256 私钥签发)
IAM-->>GW: {accessToken, refreshToken, userInfo}
GW-->>U: 200 Set-Cookie httpOnly + JSON body
```
---
## 5. 对外契约
### 5.1 REST API 路由表
| 方法 | 路径 | 鉴权 | 转发目标 | 说明 |
|------|------|------|----------|------|
| POST | /auth/login | 否 | iam | 登录签发 JWT |
| POST | /auth/refresh | 否 | iam | 刷新令牌 |
| POST | /auth/logout | 是 | iam | 登出 |
| GET | /api/classes/* | 是 | teacher-bff/student-bff | 班级相关 |
| GET | /api/courses/* | 是 | teacher-bff/student-bff | 课程相关 |
| GET | /api/assignments/* | 是 | teacher-bff/student-bff | 作业相关 |
| GET | /api/grades/* | 是 | teacher-bff/student-bff | 成绩相关 |
| GET | /api/exams/* | 是 | teacher-bff/student-bff | 考试相关 |
| GET | /api/messages/* | 是 | teacher-bff/student-bff | 消息相关 |
| GET | /api/insights/* | 是 | teacher-bff/student-bff | 学情分析 |
| GET | /healthz | 否 | 本地 | 存活探针 |
| GET | /readyz | 否 | 本地 | 就绪探针 |
| GET | /metrics | 否 | 本地 | Prometheus 指标 |
### 5.2 gRPC 服务契约
api-gateway 不对外暴露 gRPC 服务,仅作为 gRPC 客户端调用下游。
### 5.3 事件契约
api-gateway 不发布也不消费任何 Kafka 事件。
---
## 6. 依赖关系
### 6.1 上游依赖(调用方)
| 上游 | 协议 | 说明 |
|------|------|------|
| teacher-portal | HTTP/REST | 教师端微前端 |
| student-portal | HTTP/REST | 学生端微前端 |
| parent-portal | HTTP/REST | 家长端微前端 |
| admin-portal | HTTP/REST | 管理端微前端 |
### 6.2 下游依赖(被调用方)
| 下游 | 协议 | 说明 |
|------|------|------|
| teacher-bff | gRPC | 教师聚合层 |
| student-bff | gRPC | 学生聚合层 |
| parent-bff | gRPC | 家长聚合层 |
| iam | gRPC | 登录/刷新令牌特殊路由 |
| Redis | TCP | 限流计数、JWKS 缓存 |
| IAM JWKS | HTTP | RS256 公钥拉取5 分钟缓存) |
### 6.3 共享包依赖
| 包 | 用途 |
|----|------|
| shared-proto | gRPC stub 与 protobuf 类型 |
| shared-go | Go 通用工具(错误码、日志字段约定) |
---
## 7. 架构约束
1. **无业务逻辑**:网关只做路由、鉴权、限流、熔断,禁止写任何业务规则
2. **无状态**:不持有业务状态,会话状态由 Redis 管理,支持水平扩缩容
3. **无数据库**:禁止直连任何业务数据库
4. **单一职责**所有横切关注点以中间件形式实现顺序明确CORS → 日志 → 追踪 → 鉴权 → 限流 → 熔断)
5. **配置驱动**:路由表、限流阈值、熔断阈值通过 viper 配置注入,支持热更新
6. **可观测性强制**:每个请求必须生成 traceId记录 userId/latency/statusCode
7. **优雅停机**:收到 SIGTERM 后停止接受新请求,等待已有请求完成(最长 30s
---
## 8. 架构决策
### ADR-001选用 Go + Gin 而非 Node.js
**决策**:网关层使用 Go 1.22 + Gin 框架。
**理由**
- 网关是 IO 密集型场景Go 的 goroutine 模型在高并发下内存占用极低
- 单实例可承载 10k+ QPS是 Node.js 的 3-5 倍
- 编译为单二进制,容器镜像 < 20MB启动 < 1s
**替代方案**Node.js (Fastify) - 内存占用高、GC 压力大,不适合作纯转发层。
### ADR-002JWT 签名算法从 HS256 迁移到 RS256
**决策**P1 阶段使用 HS256共享密钥快速验证P2 阶段迁移到 RS256非对称签名
**理由**
- RS256 允许 IAM 私钥签发、网关/服务公钥校验,密钥不需共享
- 公钥可通过 JWKS endpoint 分发,轮换密钥无需重启服务
- HS256 适合 P1 单体调试,但生产环境密钥分发风险高
### ADR-003限流使用 Redis 令牌桶而非本地内存
**决策**:限流计数器存储在 Redis使用令牌桶算法。
**理由**
- 多副本部署下,本地内存限流无法保证全局配额
- Redis 原子操作INCR + EXPIRE保证计数准确性
- 令牌桶支持突发流量,优于漏桶的严格匀速
### ADR-004路由配置通过 viper 静态注入而非服务发现
**决策**P1 阶段路由表通过 yaml/env 静态配置,不引入服务发现。
**理由**
- P1 服务数量少api-gateway + classes静态配置足够
- 服务发现Consul/Nacos增加运维复杂度留待 P6 引入
- viper 支持热重载,路由变更无需重启
### ADR-005gRPC 转发而非 HTTP 代理
**决策**:网关到下游 BFF/服务使用 gRPC 而非 HTTP 反向代理。
**理由**
- gRPC 基于 HTTP/2 多路复用,连接复用率高
- protobuf 二进制编码比 JSON 节省 30%+ 带宽
- 强类型契约,编译期发现接口变更
- Connect-RPC 兼容 HTTP/JSON 客户端,前端仍可用 REST
### ADR-006熔断器使用滑动窗口而非简单计数
**决策**:熔断器基于滑动窗口错误率,而非固定窗口计数。
**理由**
- 滑动窗口对突发错误更敏感,避免边界效应
- 半开状态可逐步放量,避免雪崩后全量恢复冲击
- Go 生态有成熟实现sony/gobreaker