Files
Edu/services/push-gateway/README.md

6.0 KiB
Raw Blame History

Push Gateway 推送网关服务

版本1.0P5 仲裁回写) 端口8081 关联:02 架构设计modules/push-gateway/README

职责

实时推送基础设施服务Go 实现),管理 WebSocket 长连接(仅 WebSocket不支持 SSE — ISSUE-001。 接收 msg 服务的推送请求HTTP /internal/* + Kafka edu.notification.requested),维护用户在线连接池, 将消息实时投递到浏览器/移动端。无 DBISSUE-005审计/落库由 msg 负责。

技术栈

  • Go 1.25+ / Gin 1.12+
  • gorilla/websocket 1.5RFC 6455 控制帧心跳)
  • segmentio/kafka-go 0.4(纯 Go无 cgo
  • redis/go-redis/v9在线 SET + Pub/Sub 跨实例 fanout
  • golang-jwt/jwt/v5 + shared-go/jwksRS256 校验5min 缓存)
  • log/slog结构化日志JSON prod / text dev
  • prometheus/client_golang9 个指标)
  • shared-gotracer/logger/env/jwks 复用 — ARB-015 §17.5

开发

go mod tidy
go run main.go   # :8081

# DevMode跳过内部鉴权接受 dev-token
DEV_MODE=true go run main.go

# 生产模式(需设置 JWT_SECRET + INTERNAL_API_TOKEN
JWT_SECRET=xxx INTERNAL_API_TOKEN=yyy go run main.go

API

方法 路径 鉴权 说明
GET /ws?token=JWT JWT RS256 WebSocket 升级(亦支持 Authorization: Bearer
POST /internal/push X-Internal-Token 定向推送msg 服务调用)
POST /internal/broadcast X-Internal-Token 广播
GET /internal/online/<userID> X-Internal-Token 查在线状态
GET /healthz liveness 探针
GET /readyz readiness 探针(软失败 — ISSUE-058
GET /metrics Prometheus 指标

环境变量

变量 默认值 说明
PUSH_GATEWAY_PORT 8081 HTTP 监听端口
DEV_MODE false 开发模式(跳过内部鉴权,接受 dev-token
JWT_SECRET DevMode 有默认) JWT 密钥(生产必填)
JWKS_URL http://localhost:50052/.well-known/jwks.json iam JWKS 端点
INTERNAL_API_TOKEN (生产必填) /internal/* 鉴权 tokenISSUE-002
REDIS_URL redis://localhost:6379/0 Redis 连接 URL
KAFKA_BROKERS localhost:9092 Kafka broker 列表(逗号分隔)
KAFKA_NOTIFICATION_TOPIC edu.notification.requested 消费 topicISSUE-053
KAFKA_CONSUMER_GROUP push-gateway consumer group
WS_ALLOWED_ORIGINS (空) WebSocket Origin 白名单(逗号分隔)
MAX_CONNS_PER_USER 5 单用户最大连接数
HEARTBEAT_INTERVAL_SECONDS 30 心跳间隔
INSTANCE_ID hostname 实例标识Redis SET 成员)
OTEL_EXPORTER_OTLP_ENDPOINT localhost:4318 OTLP 端点

WebSocket 心跳RFC 6455 控制帧)

  • 客户端每 30s 发送 WebSocket Ping 控制帧(非文本消息)
  • 服务端自动回 Ponggorilla/websocket 默认行为)
  • 服务端 SetReadDeadline(60s)60s 未收到任何帧则断开
  • 每次 Pong 刷新 Redis 在线 SET 的 TTL60s

内部包结构

internal/
├─ config/           # 环境变量配置shared-go/env
├─ hub/              # 连接池userID -> connID -> Connection
├─ ws/               # WebSocket 升级 + /internal/* HTTP API
├─ redisclient/      # Redis Pub/Sub + 在线 SETISSUE-058 启动重建)
├─ kafkaconsumer/    # Kafka 消费edu.notification.requested
├─ observability/    # slog logger + OTel tracer + Prometheus metrics
└─ health/           # /healthz + /readyz软失败

优雅关闭顺序

  1. 收到 SIGTERM → 标记 Hub closing(拒绝新连接,/readyz 返 503
  2. Hub.CloseAll():向所有在线连接发送 close 帧code=1001 going away
  3. srv.Shutdown(10s):停止 HTTP 服务drain 在途请求
  4. Kafka consumer cancel + reader.Close
  5. Redis Pub/Sub cancel + client.Close
  6. Tracer shutdownflush span

关键设计决策

  • 软失败ISSUE-058/006Redis/Kafka 故障时 /readyz 返 200 + degraded:true,不断本地 WebSocket
  • Redis SET 启动重建ISSUE-058启动时 SREM 旧 instanceID基于内存重建
  • 无 gRPCISSUE-007仅 HTTP + WebSocket
  • shared-go 复用ARB-015 §17.5tracer/logger/env/jwks 不重复实现

完整 ADR 见 02 架构设计 §14