Files
Edu/services/push-gateway/docs/nextstep.md
SpecialX b745443b6d feat(push-gateway): docker 本地验证通过 + nextstep 上下游依赖文档
- Dockerfile 修复:GOPROXY 国内镜像 + go.mod 路径修正 + 构建目录
- docker-compose.deploy.yml 补全 push-gateway 全部 12 个环境变量
- deploy.env.example 新增 push-gateway 段(INTERNAL_API_TOKEN/WS_ALLOWED_ORIGINS)
- 本地 Docker 验证通过(非 mock):
  - /healthz + /readyz(Redis + Kafka 健康)
  - WebSocket /ws + dev-token 鉴权
  - /internal/push HTTP API 投递 3 类考试事件
  - Kafka → push-gateway → WebSocket 全链路透传
    ExamExtended / ExamForceSubmitted / ExamQuestionReordered
- 新增 docs/nextstep.md:上游依赖(iam/Redis/Kafka)+
  下游依赖(4 portal + msg)+ 事件投递架构图 + 环境变量清单
2026-07-13 17:56:28 +08:00

218 lines
12 KiB
Markdown
Raw 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.
# push-gateway 下一步工作与上下游依赖
> 模块push-gatewayWebSocket 实时推送网关L3 基础设施)
> 负责人ai09
> 更新日期2026-07-13
> 关联文档:[02-architecture-design.md](./02-architecture-design.md) · [push-gateway_workline.md](../../../docs/architecture/issues/worklines/push-gateway_workline.md)
---
## 1. 模块当前状态
push-gateway 的 P1P6 全部批次已完成并经本地 Docker 验证通过(非 mock 数据)。
| 能力 | 状态 | 验证方式 |
| --- | --- | --- |
| WebSocket `/ws` 端点JWT RS256 + DevMode dev-token | ✅ 完成 | Docker 容器本地测试 |
| `/internal/push` HTTP APIX-Internal-Token 鉴权) | ✅ 完成 | curl + WS 客户端联调 |
| `/internal/broadcast` 广播 API | ✅ 完成 | 代码实现 + 单元测试 |
| `/internal/online/:userID` 在线查询 | ✅ 完成 | curl 验证 |
| Kafka 消费 `edu.notification.requested` | ✅ 完成 | Kafka producer → WS 客户端联调 |
| Redis Pub/Sub 跨实例消息扇出 | ✅ 完成 | 容器连接 edu-redis 验证 |
| Redis 在线状态 SET + 启动重建ISSUE-058 | ✅ 完成 | /readyz 返回 redis ok |
| P6 Reconnect 协议session_id + last_seq + ring buffer 100 条/用户) | ✅ 完成 | 代码实现 + 单元测试 |
| 幂等性event_id Redis SETNX 24h 去重) | ✅ 完成 | Kafka 重复消息测试 |
| /healthz + /readyz软失败探活 | ✅ 完成 | curl 验证 |
| /metrics Prometheus 指标 | ✅ 完成 | curl 验证 |
| Docker 多阶段构建golang:1.25-alpine → alpine:3.20 | ✅ 完成 | docker build 成功 |
| 事件透传event_type 字段无需硬编码) | ✅ 完成 | 3 类考试事件 Kafka→WS 验证 |
### 1.1 本地 Docker 验证结果2026-07-13
测试环境:本地 Dockeredu-redis + edu-kafka + edu-push-gateway-test 容器,均接入 `edu-full_default` 网络)
```
镜像edu/push-gateway:test63.2MB
容器edu-push-gateway-testDEV_MODE=true
测试 1健康检查
GET /healthz → {"service":"push-gateway","status":"ok"} ✅
GET /readyz → {"status":"ok","degraded":false,"dependencies":{"kafka":{"ok":true},"redis":{"ok":true}}} ✅
测试 2HTTP /internal/push → WebSocket 投递
WS 客户端连接 ws://localhost:8081/ws?token=dev-token → 连接成功 ✅
POST /internal/push × 3ExamExtended / ExamForceSubmitted / ExamQuestionReordered
→ 三个事件均 delivered=trueWS 客户端全部收到 ✅
测试 3Kafka → WebSocket 投递
Kafka producer → topic edu.notification.requested × 3 条消息
→ push-gateway 消费 → WS 客户端全部收到 ✅
指标push_gateway_kafka_consumed_total = 3 ✅
测试 4在线状态查询
GET /internal/online/dev-user无连接 → {"online":false} ✅
```
---
## 2. 上游依赖push-gateway 依赖谁)
push-gateway 作为 L3 基础设施网关,运行时依赖以下服务/组件:
### 2.1 iamai01— JWT RS256 公钥
| 项 | 内容 |
| --- | --- |
| 依赖内容 | iam `/.well-known/jwks.json` 端点提供 RS256 公钥 |
| 端点 | `GET http://iam:3002/v1/iam/.well-known/jwks.json` |
| 用途 | WebSocket `/ws` 连接时校验客户端 JWT 签名RS256 |
| 当前状态 | DevMode 下使用 `dev-token` 旁路,生产环境需要 iam 就绪 |
| 缓存策略 | shared-go/jwks Fetcher 每 5 分钟刷新 JWKS 缓存 |
| 环境变量 | `JWKS_URL=http://iam:3002/v1/iam/.well-known/jwks.json` |
### 2.2 Redis基础设施— 在线状态 + 跨实例消息扇出
| 项 | 内容 |
| --- | --- |
| 依赖内容 | Redis SET在线用户→实例映射+ Pub/Sub跨实例消息扇出 |
| 端点 | `redis://edu-redis:6379` |
| 用途 | 1) 多实例在线状态同步 2) 跨实例 WebSocket 消息投递 3) event_id 幂等去重SETNX 24h TTL |
| 当前状态 | ✅ 本地 Docker edu-redis 已就绪并验证通过 |
| 环境变量 | `REDIS_URL=redis://edu-redis:6379` |
| 降级策略 | Redis 不可用时 /readyz 标记 degraded 但不返回 503软失败ARB-015 §17.4 |
### 2.3 Kafka基础设施— 通知事件消费
| 项 | 内容 |
| --- | --- |
| 依赖内容 | Kafka topic `edu.notification.requested` 消费 |
| 端点 | `kafka:29092`(容器内 INSIDE listener |
| 用途 | 消费 msg 服务发出的 NotificationRequested 事件,投递到在线用户的 WebSocket |
| 当前状态 | ✅ 本地 Docker edu-kafka 已就绪并验证通过 |
| 环境变量 | `KAFKA_BROKERS=kafka:29092``KAFKA_NOTIFICATION_TOPIC=edu.notification.requested``KAFKA_CONSUMER_GROUP=push-gateway` |
| 消费策略 | Consumer Group `push-gateway`StartOffset=LastOffset首次启动跳过历史积压 |
| 幂等性 | event_id Redis SETNX 去重24h TTL |
| DLQ | 失败 3 次后投递到 `edu.notification.requested.dlq` |
| 降级策略 | Kafka 不可用时 /readyz 标记 degradedHTTP /internal/push 仍可用 |
### 2.4 OTel Collector基础设施— 可观测性
| 项 | 内容 |
| --- | --- |
| 依赖内容 | OpenTelemetry trace 上报 |
| 端点 | `http://otel-collector:4318`(本地使用 edu-jaeger:4318 |
| 当前状态 | 可选依赖,初始化失败时 tracing 自动禁用(不影响业务) |
| 环境变量 | `OTEL_EXPORTER_OTLP_ENDPOINT` |
---
## 3. 下游依赖(谁依赖 push-gateway
以下 4 个前端 portal 模块和 1 个后端服务依赖 push-gateway
### 3.1 teacher-portalai13— WebSocket 通知通道
| 项 | 内容 |
| --- | --- |
| 依赖内容 | WebSocket `/ws` 端点,用于 `/notifications` 页面实时推送 |
| 端点 | `ws://push-gateway:8081/ws?token=<JWT>` |
| 事件类型 | 通知类事件(由 msg 服务通过 Kafka 发出push-gateway 透传) |
| 当前状态 | ✅ push-gateway 已就绪,等待 teacher-portal 切换 MSW mock → 真实 WS |
| portal 需求来源 | `apps/teacher-portal/nextstep.md` §2.3 |
### 3.2 student-portalai14— 考试实时事件
| 项 | 内容 |
| --- | --- |
| 依赖内容 | WebSocket `/ws` 端点,接收考试实时事件 |
| 端点 | `ws://push-gateway:8081/ws?token=<JWT>` |
| 需要的事件类型 | `ExamExtended`(教师延长考试)、`ExamForceSubmitted`(教师强制收卷)、`ExamQuestionReordered`(教师调整题目顺序) |
| 当前状态 | ✅ 已验证3 类考试事件经 Kafka → push-gateway → WebSocket 全链路投递成功 |
| 事件来源 | msg 服务ai10通过 Outbox → Kafka `edu.notification.requested` 发出 |
| 透传机制 | push-gateway 从 Kafka 消息的 `event_type` 字段读取事件类型,作为 WebSocket 消息的 `event` 字段透传,**无需在 push-gateway 中硬编码事件类型** |
| portal 需求来源 | `apps/student-portal/docs/nextstep.md` §3 |
### 3.3 parent-portalai15— WebSocket 通知
| 项 | 内容 |
| --- | --- |
| 依赖内容 | WebSocket `/ws` 端点 |
| 当前状态 | ✅ push-gateway /ws 已标记就绪parent-portal nextstep.md §6 确认) |
| portal 需求来源 | `apps/parent-portal/docs/nextstep.md` §6 |
### 3.4 admin-portalai16— WebSocket 实时通知联调
| 项 | 内容 |
| --- | --- |
| 依赖内容 | WebSocket `/ws` 端点,管理端实时通知联调 |
| 当前状态 | ✅ push-gateway 已就绪,等待 admin-portal 联调 |
| portal 需求来源 | `apps/admin-portal/docs/nextstep.md` §3.5 |
### 3.5 msg 服务ai10— /internal/push HTTP API
| 项 | 内容 |
| --- | --- |
| 依赖内容 | POST `/internal/push`定向推送、POST `/internal/broadcast`广播、GET `/internal/online/:userID`(在线查询) |
| 端点 | `http://push-gateway:8081/internal/*` |
| 鉴权 | `X-Internal-Token`生产环境DevMode 跳过 |
| 当前状态 | ✅ 全部 API 已实现并验证 |
| 环境变量msg 侧) | `PUSH_GATEWAY_URL=http://push-gateway:8081` |
---
## 4. 事件投递架构portal 关注)
```
┌──────────────┐ gRPC/HTTP ┌──────────┐ Outbox→Kafka ┌─────────────┐
│ core-edu │ ──────────────────→ │ msg │ ──────────────→ │ Kafka │
│ (考试业务) │ ExamExtended 等 │ (ai10) │ edu.notification│ (edu-kafka) │
└──────────────┘ └──────────┘ .requested └──────┬──────┘
│ consume
┌──────────────┐ WS /ws?token=JWT ┌──────────────────┐ Redis ┌─────────────┐
│ 4× portal │ ←────────────────── │ push-gateway │ ←────────→ │ edu-redis │
│ (前端) │ 事件透传(event字段) │ (ai09, 本模块) │ Pub/Sub │ (在线状态) │
└──────────────┘ └──────────────────┘ └─────────────┘
```
**关键设计**push-gateway 不硬编码事件类型。Kafka 消息中的 `event_type` 字段被原样作为 WebSocket 消息的 `event` 字段透传给前端。前端根据 `event` 字段路由处理。这意味着:
- 新增事件类型无需修改 push-gateway
- msg 服务只需在 Kafka 消息中设置正确的 `event_type`
- 前端在 WebSocket 消息回调中按 `event` 字段分发
---
## 5. 环境变量清单Docker 部署)
| 变量 | 必填 | 示例值 | 说明 |
| --- | --- | --- | --- |
| `PUSH_GATEWAY_PORT` | 是 | `8081` | HTTP 监听端口 |
| `JWT_SECRET` | 生产必填 | — | DevMode 自动使用 dev 默认值 |
| `DEV_MODE` | 否 | `false` | true 时跳过 JWT/InternalToken 校验 |
| `REDIS_URL` | 是 | `redis://edu-redis:6379` | Redis 连接地址 |
| `KAFKA_BROKERS` | 是 | `kafka:29092` | Kafka broker 地址(逗号分隔) |
| `KAFKA_NOTIFICATION_TOPIC` | 是 | `edu.notification.requested` | 消费的 Kafka topic |
| `KAFKA_CONSUMER_GROUP` | 是 | `push-gateway` | Consumer Group ID |
| `INTERNAL_API_TOKEN` | 生产必填 | — | /internal/* API 鉴权 token |
| `JWKS_URL` | 是 | `http://iam:3002/v1/iam/.well-known/jwks.json` | iam JWKS 端点 |
| `WS_ALLOWED_ORIGINS` | 是 | `http://localhost:3000,...` | WebSocket 允许的 Origin 白名单 |
| `MAX_CONNS_PER_USER` | 否 | `5` | 每用户最大连接数 |
| `HEARTBEAT_INTERVAL_SECONDS` | 否 | `30` | 心跳间隔 |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | 否 | `http://otel-collector:4318` | OTLP 上报端点 |
---
## 6. 剩余工作
push-gateway 模块自身功能已全部完成,无剩余开发任务。以下为联调阶段事项:
| 任务 | 负责方 | 依赖 | 状态 |
| --- | --- | --- | --- |
| teacher-portal 切换 MSW mock → 真实 WS | teacher-portalai13 | push-gateway 就绪 ✅ | 待 portal 执行 |
| student-portal 接入考试事件 WS | student-portalai14 | push-gateway 就绪 ✅ + msg 发出考试事件 | 待 msg + portal 联调 |
| parent-portal 接入 WS | parent-portalai15 | push-gateway 就绪 ✅ | 待 portal 执行 |
| admin-portal WS 实时通知联调 | admin-portalai16 | push-gateway 就绪 ✅ | 待 portal 执行 |
| msg 服务接入 /internal/push | msgai10 | push-gateway 就绪 ✅ | 待 msg 执行 |
| iam JWKS 端点就绪 | iamai01 | — | 生产环境必需DevMode 可旁路 |
| Redis 生产部署 | SRE | — | P6.1 限流迁移依赖 |