chore(push-gateway): ai02 module updates - config, hub, ws, kafka, health, docs
This commit is contained in:
@@ -364,14 +364,29 @@
|
||||
|
||||
### 2.8 push-gateway(Go,P5)
|
||||
|
||||
| 场景 | 技术/规则 |
|
||||
| ---------------- | ---------------------------------------------------------------- |
|
||||
| WebSocket 长连接 | 单节点支撑 10w+ 连接,业务服务只需调 /internal/push |
|
||||
| 跨实例同步 | Redis PubSub(RedisURL 配置,预留 P6 实现) |
|
||||
| 离线消息 | 仅推在线用户,离线消息存 MySQL,上线时拉取 |
|
||||
| 并发写修复 | send chan + 单写协程模式,避免 gorilla/websocket 并发写竞争 |
|
||||
| DEV_MODE 鉴权 | DEV_MODE=true 时接受 dev-token,生产环境必须 JWT 校验 |
|
||||
| 广播端点 | POST /internal/broadcast,body {event, data},调用 hub.Broadcast |
|
||||
| 场景 | 技术/规则 |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| WebSocket 长连接 | 单实例支撑 10w+ 连接(ISSUE-003),业务服务调 /internal/push 或消费 Kafka |
|
||||
| 心跳协议 | RFC 6455 控制帧(Ping/Pong),30s 间隔,SetReadDeadline(60s) 超时断开;禁止文本消息 ping/pong |
|
||||
| 单用户连接数限制 | 默认 5,超限返 PUSH_TOO_MANY_CONNECTIONS + close frame 1008 |
|
||||
| 跨实例同步 | Redis Pub/Sub 订阅 `edu:push:channel:user:<userID>` + `edu:push:channel:broadcast`;在线 SET `edu:push:online:<userID>` 60s TTL |
|
||||
| Redis SET 启动重建(ISSUE-058) | 启动时 SREM 旧 instanceID 成员,基于内存连接 ForEachUser 重新 SADD,避免 Pod 重启后残留幽灵在线状态 |
|
||||
| 离线消息 | 仅推在线用户;离线消息落库由 msg 服务负责(ISSUE-005),push-gateway 无 DB |
|
||||
| 并发写修复 | send chan + 单写协程模式,避免 gorilla/websocket 并发写竞争 |
|
||||
| 内部 API 鉴权 | `X-Internal-Token` 头 + `INTERNAL_API_TOKEN` 环境变量(ISSUE-002,原名 X-Internal-Key 已废弃) |
|
||||
| Kafka topic 命名(ISSUE-053) | `edu.notification.requested`(非 edu.notification.events,遵循 G16 `<domain>.<aggregate>.<action>` 规则) |
|
||||
| Kafka 幂等性 | 基于 event_id Redis SETNX 去重(24h TTL),at-least-once + 消费失败 3 次入 DLQ |
|
||||
| /readyz 软失败(ISSUE-058/006) | Redis/Kafka 不可达返 200 + `degraded: true`,仅 Hub.CloseAll 触发 closing 时返 503;避免 K8s 在依赖抖动时驱逐 Pod |
|
||||
| 无 gRPC(ISSUE-007) | 仅 HTTP + WebSocket,不暴露 gRPC 服务 |
|
||||
| shared-go 复用(ARB-015 §17.5) | tracer/logger/jwks/env 走 shared-go 包,不重复实现 |
|
||||
| 错误码前缀 | `PUSH_*`(PUSH_UNAUTHORIZED/PUSH_INVALID_REQUEST/PUSH_TOO_MANY_CONNECTIONS 等) |
|
||||
| 多阶段 Dockerfile | golang:1.25-alpine 构建 → alpine:3.20 运行,CGO_ENABLED=0 + ldflags -s -w + non-root user + wget healthcheck |
|
||||
| slog 结构化日志 | log/slog(JSON prod / text dev),替代标准 log 包;字段含 request_id/trace_id/user_id/conn_id/event |
|
||||
| DEV_MODE 鉴权 | DEV_MODE=true 时接受 dev-token(HS256 fallback),生产环境必须 JWT RS256(JWKS 5min 缓存) |
|
||||
| 广播端点 | POST /internal/broadcast,body {event, data},调用 hub.Broadcast |
|
||||
| gorilla/websocket API 陷阱 | SetReadLimit 返回 void(不可 `_ =` 赋值);SetPongHandler 签名为 `func(appData string) error`(非 `func(string, error) error`) |
|
||||
| sync.RWMutex 重入死锁 | 持写锁(Lock)的 goroutine 不可再调用同结构体的 RLock 方法(Go RWMutex 非重入);CloseAll/Broadcast 内部计数改为内联遍历 |
|
||||
| httptest.Server.Close() 死锁 | 测试 handler 阻塞 ReadMessage 时 Close() 会死锁;用 done channel 模式:cleanup 先 close(done) → cli.Close() → srv.Close() |
|
||||
|
||||
### 2.9 ai-gateway(Python/FastAPI,P5)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user