# 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
限流计数)] 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 令牌桶
剩余令牌 >= 1?} Q1 -- 是 --> DEC[扣减令牌
INCR + EXPIRE] DEC --> PASS[放行] Q1 -- 否 --> Q2{是否超量请求?} Q2 -- 是 --> REJ1[429 Too Many Requests
Retry-After header] Q2 -- 否 --> WAIT[排队等待
短超时] 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: 生成 JWT(RS256 私钥签发) 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-002:JWT 签名算法从 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-005:gRPC 转发而非 HTTP 代理 **决策**:网关到下游 BFF/服务使用 gRPC 而非 HTTP 反向代理。 **理由**: - gRPC 基于 HTTP/2 多路复用,连接复用率高 - protobuf 二进制编码比 JSON 节省 30%+ 带宽 - 强类型契约,编译期发现接口变更 - Connect-RPC 兼容 HTTP/JSON 客户端,前端仍可用 REST ### ADR-006:熔断器使用滑动窗口而非简单计数 **决策**:熔断器基于滑动窗口错误率,而非固定窗口计数。 **理由**: - 滑动窗口对突发错误更敏感,避免边界效应 - 半开状态可逐步放量,避免雪崩后全量恢复冲击 - Go 生态有成熟实现(sony/gobreaker)