# 阶段 0 技术 Spike 最小工程计划 ## 目标 阶段 0 只验证第一版最大技术风险:NestJS 入队、Go Gateway 消费、模拟 CMPP 提交、submit resp、deliver 回执、上行事件回传、断线重连和 500 条/秒链路能力。 本阶段不实现完整业务审核、风控、计费、报备、路由后台,也不从零手写完整 CMPP 协议栈。 ## 1. 目录结构建议 当前仓库先保留现有 React + TypeScript + Vite 原型作为根目录前端。阶段 0 建议以最小增量方式补齐后端和网关 Spike 目录: ```text . ├── docs/ │ ├── contracts/ │ │ ├── gateway-queue-messages.schema.json │ │ └── examples/ │ ├── phase-0-technical-spike-plan.md │ └── phase-0-spike-progress.md ├── api/ │ ├── README.md │ └── src/ │ └── spike/ │ └── queue-contracts/ ├── gateway/ │ ├── README.md │ ├── cmd/ │ │ ├── gateway/ │ │ └── smsc-simulator/ │ └── internal/ │ ├── cmpp/ │ ├── queue/ │ ├── connection/ │ ├── tracker/ │ └── metrics/ └── tools/ └── spike/ └── validate-gateway-queue-contract.mjs ``` 阶段 1 再决定是否迁移为正式 monorepo: - `web/`:当前 React 原型。 - `api/`:NestJS + Prisma + BullMQ。 - `gateway/`:Go CMPP Gateway。 - `infra/`:PostgreSQL、Redis、MinIO、Prometheus、Grafana 的 compose 与部署配置。 ## 2. NestJS 与 Go Gateway 队列消息格式 阶段 0 采用 Redis + BullMQ 作为通信基线。NestJS 只负责投递已经完成业务校验和路由决策后的发送指令;Go Gateway 只负责协议提交和事件回传。 ### 队列命名 - `cmpp.submit.commands`:NestJS -> Go Gateway,单号码发送指令。 - `cmpp.submit.results`:Go Gateway -> NestJS,submit resp 结果。 - `cmpp.receipt.events`:Go Gateway -> NestJS,deliver 回执事件。 - `cmpp.uplink.events`:Go Gateway -> NestJS,上行短信事件。 ### 统一信封 每条消息必须包含: - `schemaVersion`:当前固定为 `v1`。 - `messageType`:消息类型。 - `traceId`:贯穿 API、队列、网关、回执的链路 ID。 - `messageId`:平台单号码短信 ID,全局唯一。 - `channelId`:业务后台选定的通道 ID。 - `createdAt`:ISO 8601 时间。 ### 发送指令 `SubmitCommand` 由 NestJS 或 Send Worker 产生。必须包含 `tenantId`、`applicationId`、`submitId`、`phoneNumber`、`content`、`signature`、`templateId`、`billingUnits`、`route`、`cmpp` 和 `retry`。 Go Gateway 不根据签名、模板、账户余额做业务判断,只读取 `route.cmppAccountCode`、`cmpp.serviceId`、`cmpp.srcId`、`cmpp.registeredDelivery`、`cmpp.msgFmt` 等协议提交所需字段。 ### 结果和事件 - `SubmitResult` 必须回传 `sequenceId`、`gatewayMessageId`、`submitStatus`、`submittedAt`。 - `ReceiptEvent` 必须回传 `gatewayMessageId`、`sequenceId`、`receiptStatus`、`deliveredAt`、`rawStatus`。 - `UplinkEvent` 必须回传 `phoneNumber`、`destId`、`content`、`receivedAt`、`sequenceId`。 详细结构以 `docs/contracts/gateway-queue-messages.schema.json` 为准。 ## 3. Go Gateway 最小能力清单 阶段 0 最小 Go Gateway 只做以下能力: 1. 读取一个通道配置:网关地址、端口、企业代码、账号、密码、接入号、CMPP 版本、窗口大小、心跳间隔、重连间隔。 2. 基于 gocmpp 或评估后的协议库完成 connect、active test、submit、deliver、terminate。 3. 消费 `cmpp.submit.commands`,提交到模拟 SMSC。 4. 将 submit resp 写入 `cmpp.submit.results`。 5. 将 deliver 回执写入 `cmpp.receipt.events`。 6. 将上行短信写入 `cmpp.uplink.events`。 7. 维护 `messageId -> sequenceId -> gatewayMessageId` 的追踪映射。 8. 支持断线重连,重连后继续消费后续消息。 9. 暴露最小健康检查和指标:连接状态、队列积压、提交 TPS、submit 成功率、回执延迟、重连次数。 10. 支持模拟 SMSC:正常响应、慢响应、断线、重复回执、窗口满。 ## 4. gocmpp 与 cmpp-gateway 技术评估任务 评估对象: - `bigwhite/gocmpp`:优先评估为协议层依赖。 - `JoeCao/cmpp-gateway`:只作为连接管理、重连、SEQID/MSGID 追踪和模拟器设计参考。 任务清单: 1. License:确认依赖许可是否允许第一版商业项目使用。 2. 维护活跃度:确认最近提交、Issue、PR、Go module 支持情况。 3. 协议覆盖:确认 CMPP 2.0/3.0、connect、submit、deliver、active test、terminate 支持情况。 4. 编解码可靠性:验证长短信、UCS2、GBK、状态报告、上行短信解析。 5. 连接模型:评估长连接、心跳、窗口、并发 submit、断线重连。 6. 追踪能力:验证 sequenceId、msgId、平台 messageId 的映射方案。 7. 模拟器:复用或参考模拟 SMSC 的响应、慢响应、断线和重复回执能力。 8. 改造边界:判断直接依赖、轻量 fork、或只参考实现的取舍。 9. 风险清单:列出协议层缺口、生产部署风险、测试补充项。 ## 5. 500 条/秒 Spike 压测指标 压测只验证链路调度能力,不验证完整业务规则。 ### 场景 1. NestJS 批量投递 30 秒,共 15000 条发送指令。 2. Go Gateway 消费队列并提交到模拟 SMSC。 3. 模拟 SMSC 立即返回 submit resp,并在 1 到 3 秒内返回 deliver。 4. 重复执行单连接和多连接两个场景。 5. 插入异常场景:模拟 SMSC 慢响应、短暂断线、重复回执。 ### 指标 - 入队吞吐:P50、P95、P99,每秒入队数必须稳定达到 500。 - 消费吞吐:每秒消费并提交数,30 秒平均不低于 500。 - 端到端延迟:入队到 submit resp P95 小于 2 秒。 - 回执延迟:deliver 到 NestJS 消费 P95 小于 10 秒。 - 错误率:submit command 处理失败率小于 0.1%。 - 重试率:异常场景下可观测且不造成重复最终状态。 - 队列积压:压测停止后 30 秒内归零。 - 资源指标:CPU、内存、Redis ops、连接重连次数。 - 幂等指标:重复回执只产生一条最终状态更新,多余回执进入历史记录。 ## 6. 阶段 0 验收标准 1. 跑通一条模拟短信链路:NestJS 入队 -> Go Gateway 提交 -> submit resp -> deliver 回执 -> NestJS 更新状态。 2. Go Gateway 断线后可重连,并能继续消费后续消息。 3. 所有消息具备 `traceId`、`messageId`、`channelId`、`sequenceId` 或可追踪映射。 4. 队列消息 Schema、示例消息和校验脚本通过。 5. 完成 gocmpp 与 JoeCao/cmpp-gateway 评估记录,明确最终依赖方式。 6. 完成 500 条/秒压测报告,包含瓶颈、资源使用和下一阶段优化建议。 7. 文档记录 Go Gateway 哪些能力复用协议库,哪些服务层能力由本项目自研。 ## 阶段 0 执行顺序 1. 创建阶段 0 计划文档、队列消息 Schema、示例消息和校验脚本。 2. 创建 Go Gateway Spike 骨架和模拟 SMSC 骨架。 3. 创建 NestJS Spike 骨架,完成 BullMQ 入队和事件消费。 4. 接入 Redis 本地环境,跑通模拟链路。 5. 评估 gocmpp 与 JoeCao/cmpp-gateway。 6. 完成 500 条/秒压测脚本和报告。