Files
Edu/services/push-gateway/docs/nextstep-v2.md

25 KiB
Raw Blame History

push-gateway 模块上下游依赖与工作清单Next Steps v2

版本v2 日期2026-07-14 负责人ai09 关联:


1. 概述

push-gateway 是 Edu 平台的实时 WebSocket 推送基础设施L3 网关层,端口 8081负责维护用户在线连接、接收 msg 服务通过 HTTP /internal/* 和 Kafka 推送的消息、并通过 WebSocket 实时投递给在线客户端。

v2 核心目标:对齐 ARB-013 Kafka topic 命名规范和 msg 服务的 API 契约,消除 v1 中存在的 4 个不一致点。


2. v2 完成的工作API 契约对齐)

2.1 Kafka topic 对齐ARB-013

v1 v2 状态
消费 topic edu.notification.requested edu.notify.notification.sent
命名规范 旧命名(违反 ARB-013 edu.<domain>.<aggregate>.<action>
DLQ topic edu.notification.requested.dlq edu.notify.notification.sent.dlq(自动派生)

修改文件

  • internal/config/config.goKAFKA_NOTIFICATION_TOPIC 默认值改为 edu.notify.notification.sent
  • internal/kafkaconsumer/consumer.go包注释更新metric label 使用 event.EventType(而非硬编码 "notification.requested"
  • internal/config/config_test.go:断言更新为新 topic 名

2.2 鉴权头对齐

v1 v2 状态
规范头名 X-Internal-Token X-Internal-KeyARB-013 对齐 msg
向后兼容 同时接受 X-Internal-Tokenlegacy alias
环境变量 INTERNAL_API_TOKEN PUSH_INTERNAL_TOKENcanonical+ INTERNAL_API_TOKENfallback

修改文件

  • internal/ws/handler.go
    • internalTokenHeader 常量改为 X-Internal-Key
    • 新增 legacyInternalTokenHeader = "X-Internal-Token" 兼容别名
    • checkInternalToken() 依次尝试两个头
  • internal/config/config.go:优先读取 PUSH_INTERNAL_TOKEN,为空时回退到 INTERNAL_API_TOKEN

2.3 请求体字段命名对齐

v1 v2 状态
用户 ID 字段 user_idsnake_case userIdcamelCasemsg canonical+ user_idlegacy
其他字段 event / data / ttl 保持不变

修改文件

  • internal/ws/handler.go PushHandler()
    • 结构体同时定义 UserID json:"userId"UserIDLeg json:"user_id"
    • 绑定后若 UserID 为空则回退到 UserIDLeg

2.4 环境变量对齐

v1 环境变量 v2 规范环境变量 兼容性
INTERNAL_API_TOKEN PUSH_INTERNAL_TOKEN v1 变量作为 fallback 别名保留
KAFKA_NOTIFICATION_TOPIC(默认 edu.notification.requested KAFKA_NOTIFICATION_TOPIC(默认 edu.notify.notification.sent 环境变量名不变,默认值更新

修改文件

  • infra/docker-compose.deploy.ymlpush-gateway 段使用 PUSH_INTERNAL_TOKEN(支持 ${PUSH_INTERNAL_TOKEN:-${INTERNAL_API_TOKEN:-edu-internal-token}} 回退链)
  • infra/deploy.env.examplePUSH_INTERNAL_TOKEN 为规范变量,INTERNAL_API_TOKEN 标注为兼容别名

2.5 部署配置更新

文件 修改内容
infra/docker-compose.deploy.yml KAFKA_NOTIFICATION_TOPIC: edu.notify.notification.sentPUSH_INTERNAL_TOKEN 带回退链
infra/deploy.env.example 新增 PUSH_INTERNAL_TOKEN,保留 INTERNAL_API_TOKEN 作为兼容别名(留空)

3. 本地 Docker 验证结果2026-07-14 v2

测试环境:本地 Docker Desktopedu-full_default 网络,接入真实 edu-kafka / edu-redis / edu-iam-test。

镜像edu/push-gateway:test
容器edu-push-gateway-testDEV_MODE=true, PUSH_INTERNAL_TOKEN=test-internal-token-v2
网络edu-full_default直连 edu-kafka:29092 / edu-redis:6379 / edu-iam-test:3002
验证项 结果 说明
go vet ./... 零警告
go build ./... 零错误
go test ./... config/hub/ws 全部通过(含新增 TestLoadProdModeLegacyToken
Docker 镜像构建 edu/push-gateway:test 构建成功
/healthz {"service":"push-gateway","status":"ok"}
/readyz degraded:falsekafka+redis 均 ok
容器日志确认 topic kafka_topic=edu.notify.notification.sent
v2 契约X-Internal-Key + userId POST /internal/push 返回 {"success":true}
v1 兼容X-Internal-Token + user_id 旧调用方式仍正常工作
Kafka 全链路:新 topic → WS 发布到 edu.notify.notification.sent → WebSocket 客户端收到消息
考试事件透传3 类) ExamExtended / ExamForceSubmitted / ExamQuestionReordered 全部经 Kafka → WS 投递成功
Prometheus metrics push_gateway_kafka_consumed_total{topic="edu.notify.notification.sent"} 计数正确;push_gateway_messages_pushed_total{event="ExamExtended",result="delivered"} 等标签正确

3.1 测试脚本

脚本 用途
services/push-gateway/scripts/test-kafka-ws.mjs 验证单条 Kafka 消息经 edu.notify.notification.sent topic 到达 WebSocket 客户端
services/push-gateway/scripts/test-exam-events.mjs 验证 3 类考试事件ExamExtended/ExamForceSubmitted/ExamQuestionReordered的 event_type 透传

运行方式(需在仓库根目录):

# 前置push-gateway 容器已启动并接入 edu-full_default 网络
node services/push-gateway/scripts/test-kafka-ws.mjs
node services/push-gateway/scripts/test-exam-events.mjs

4. 上游依赖(调用 push-gateway 的模块)

4.1 4 个前端 Portalteacher/student/parent/admin— P0

Portal WebSocket 接入状态 v2 新增工作 状态
teacher-portal 已接入(useNotificationsWebSocket hook
student-portal 已接入(考试事件监听)
parent-portal 已接入
admin-portal 已接入

说明4 个 portal 在 v1 阶段已完成 WebSocket 接入,使用 ?token=<jwt> query param 鉴权。v2 的 API 契约变更Kafka topic / 鉴权头 / 请求体字段)对 portal 透明,无需 portal 侧改动。

4.2 3 个 BFFteacher-bff / student-bff / parent-bff— P1

BFF 依赖 push-gateway 的方式 v2 新增工作 状态
teacher-bff 无直接依赖BFF 不调用 push-gateway
student-bff 无直接依赖BFF 不调用 push-gateway
parent-bff 无直接依赖BFF 不调用 push-gateway

说明BFF 层不直接调用 push-gateway。推送由 msg 服务通过 HTTP /internal/push 或 Kafka 发起push-gateway 负责投递到 portal 的 WebSocket。


5. 下游依赖push-gateway 调用的模块)

5.1 iam 服务ai06 负责)— P0

依赖项 用途 状态
GET /v1/iam/.well-known/jwks.json RS256 JWKS 公钥WebSocket JWT 验签) 已就绪

环境变量JWKS_URL=http://iam:3002/v1/iam/.well-known/jwks.json

说明shared-go/jwks Fetcher 每 5 分钟刷新 JWKS 缓存。DevMode 接受 dev-token 旁路 JWT 验签。

5.2 msg 服务ai10 负责)— P0

依赖项 用途 状态
HTTP POST /internal/push 定向推送msg → push-gateway 已对齐
Kafka topic edu.notify.notification.sent 事件驱动推送msg Outbox → Kafka → push-gateway 已对齐
鉴权头 X-Internal-Key: PUSH_INTERNAL_TOKEN /internal/* API 鉴权 已对齐
请求体 { userId, event, data } camelCase 字段命名 已对齐

环境变量

  • push-gateway 侧:PUSH_INTERNAL_TOKENcanonical/ INTERNAL_API_TOKENfallback
  • msg 侧:PUSH_INTERNAL_TOKEN(已对齐)

v2 对齐结果4 个不一致点Kafka topic / 鉴权头 / 请求体字段 / 环境变量全部修复push-gateway 同时保持对 v1 调用方式的向后兼容。

5.3 Redis基础设施— P0

依赖项 用途 状态
redis://edu-redis:6379 在线状态 SET + 跨实例 Pub/Sub 扇出 + 事件去重 SETNX 已就绪

环境变量REDIS_URL=redis://edu-redis:6379/0

5.4 Kafka基础设施— P0

依赖项 用途 状态
edu-kafka:29092 消费 edu.notify.notification.sent topic 已就绪

环境变量

  • KAFKA_BROKERS=edu-kafka:29092
  • KAFKA_NOTIFICATION_TOPIC=edu.notify.notification.sentARB-013
  • KAFKA_CONSUMER_GROUP=push-gateway

5.5 OTel Collector基础设施— P1

依赖项 用途 状态
http://otel-collector:4318 OTLP trace 上报 待部署

环境变量OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318

说明:未配置时 push-gateway 跳过 trace 上报,不影响功能。


6. 待协调事项(需上下游配合)

6.1 core-edu 考试事件发布(下游阻塞 student-portal

详情
阻塞方 core-eduai07
影响方 student-portal / student-bff
问题 student-portal 期望接收 ExamExtended / ExamForceSubmitted / ExamQuestionReordered 三类考试事件,但这些事件在 core-edu 源码中未找到发布逻辑
期望流程 core-edu Outbox publisher → Kafka edu.notify.notification.sent → push-gateway → student-portal WebSocket
push-gateway 侧状态 已验证可消费这三类事件并透传到 WebSocket见 §3 测试结果)
协调动作 请 core-edu 在考试相关业务逻辑中发布这三类事件到 edu.notify.notification.sent topic

6.2 msg 服务 Outbox 发布 topic 对齐(待确认)

详情
阻塞方 msgai10
问题 需确认 msg 的 Outbox publisher 已将发布 topic 从 edu.notification.requested 改为 edu.notify.notification.sentARB-013
push-gateway 侧状态 已消费 edu.notify.notification.sent
协调动作 请 msg 确认 Outbox TOPIC_MAPNotificationSent 事件映射到 edu.notify.notification.sent;如仍发往旧 topic需更新

6.3 生产环境环境变量迁移(部署时)

详情
阻塞方 SRE AI / 人类决策者
问题 生产环境需将 INTERNAL_API_TOKEN 迁移为 PUSH_INTERNAL_TOKEN(或在 deploy.env 中同时设置两者)
push-gateway 侧状态 支持双环境变量(PUSH_INTERNAL_TOKEN 优先,INTERNAL_API_TOKEN 回退)
协调动作 部署时在 infra/security/secrets.example.env 或 K8s Secret 中设置 PUSH_INTERNAL_TOKEN;旧变量可保留作为兼容

7. v2 文件变更清单

7.1 源码修改

文件 修改内容
services/push-gateway/internal/config/config.go Kafka topic 默认值 → edu.notify.notification.sent;支持 PUSH_INTERNAL_TOKEN 环境变量(带 INTERNAL_API_TOKEN 回退)
services/push-gateway/internal/ws/handler.go 鉴权头 X-Internal-Keycanonical+ X-Internal-Tokenlegacy请求体 userIdcanonical+ user_idlegacy
services/push-gateway/internal/kafkaconsumer/consumer.go 包注释更新metric label 使用 event.EventType 而非硬编码
services/push-gateway/internal/config/config_test.go 断言新 topic 名;新增 TestLoadProdModeLegacyToken 验证 fallback
services/push-gateway/main.go 启动流程注释更新topic 名 + 鉴权头名)

7.2 部署配置修改

文件 修改内容
infra/docker-compose.deploy.yml push-gateway 段:KAFKA_NOTIFICATION_TOPIC: edu.notify.notification.sentPUSH_INTERNAL_TOKEN 带回退链
infra/deploy.env.example PUSH_INTERNAL_TOKEN 为规范变量;INTERNAL_API_TOKEN 标注为兼容别名

7.3 测试脚本新增

文件 用途
services/push-gateway/scripts/test-kafka-ws.mjs Kafka → WebSocket 全链路验证(单条消息)
services/push-gateway/scripts/test-exam-events.mjs 3 类考试事件透传验证

7.4 根 package.json

文件 修改内容
package.json devDependencies 新增 kafkajs + ws(仅用于测试脚本)

8. 环境变量清单v2

环境变量 必填 默认值 用途
PUSH_GATEWAY_PORT 8081 HTTP 端口
JWT_SECRET 生产必填 JWT HS256 签名密钥DevMode 有默认值)
DEV_MODE false 开发模式(旁路 JWT + InternalToken 校验)
REDIS_URL redis://localhost:6379/0 Redis 连接地址
KAFKA_BROKERS localhost:9092 Kafka broker 列表(逗号分隔)
KAFKA_NOTIFICATION_TOPIC edu.notify.notification.sent 消费的 Kafka topicARB-013
KAFKA_CONSUMER_GROUP push-gateway Kafka 消费者组
PUSH_INTERNAL_TOKEN 生产必填 /internal/* API 鉴权 tokenARB-013 canonical
INTERNAL_API_TOKEN 兼容别名 /internal/* API 鉴权 tokenfallbackv1 兼容)
JWKS_URL 生产必填 iam JWKS 端点RS256 公钥)
WS_ALLOWED_ORIGINS 生产必填 WebSocket 允许的 Origin 白名单(逗号分隔)
MAX_CONNS_PER_USER 5 单用户最大连接数
HEARTBEAT_INTERVAL_SECONDS 30 心跳间隔(秒)
OTEL_EXPORTER_OTLP_ENDPOINT localhost:4318 OTLP collector 端点
INSTANCE_ID hostname 实例标识Redis SET 成员)

9. API 契约v2

9.1 WebSocket 端点

GET /ws?token=<jwt>
  • 鉴权JWT RS256via iam JWKS或 DevMode dev-token
  • 消息格式:{ "type": "message", "event": "<event_type>", "data": {...}, "timestamp": "<RFC3339>" }
  • 心跳RFC 6455 Ping/Pong30s 间隔60s 空闲超时)

9.2 内部 HTTP API

方法 路径 鉴权头 请求体 响应
POST /internal/push X-Internal-Key: <token> { userId, event, data, ttl? } { success, delivered?, online?, reached?, error? }
POST /internal/broadcast X-Internal-Key: <token> { event, data, filter? } { success, reached? }
GET /internal/online/<userID> X-Internal-Key: <token> { online: bool, instances: [] }

向后兼容

  • 鉴权头同时接受 X-Internal-Tokenv1 legacy
  • /internal/push 请求体同时接受 user_idsnake_casev1 legacy

9.3 Kafka 消费

Topic 消息格式 来源
edu.notify.notification.sent NotificationRequested JSON msg Outbox publisher
edu.notify.notification.sent.dlq 原始消息(失败 3 次后投递) push-gateway 自动派生

消息字段event_id / user_id / event_type / channel / title / content / data / broadcast / occurred_at

event_type 透传push-gateway 从 Kafka 消息的 event_type 字段读取事件类型,作为 WebSocket 消息的 event 字段透传,不硬编码事件类型。


10. 后续工作v3 展望)

工作项 优先级 说明
P6 Reconnect 协议 P1 session_id + last_seq + ring buffer100 条/用户)支持断线重连后补发
P6 离线消息推送 P2 与 msg 服务集成,离线用户消息落库后通过 SMS/邮件/应用内通知补发
Prometheus 告警规则 P2 配置 push_gateway_kafka_consumed_total 停止增长告警
Grafana 面板 P2 WebSocket 连接数、Kafka 消费延迟、推送成功率面板
多副本部署验证 P2 验证 Redis Pub/Sub 跨实例扇出在多副本场景下的正确性