Files
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
..

api-gateway 网关服务

版本1.0 日期2026-07-07 状态P1 已交付 关联文档:架构影响地图


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. 架构图

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 鉴权与路由流程

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 限流流程

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 登录特殊处理流程

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