# CMPP Gateway 容量扩展与回调降载方案 更新日期:2026-08-25 状态:已实施并完成测试环境验证 适用范围:发送 Worker/Submit Outbox 到供应商 CMPP Submit、SubmitResp、状态报告、上行和计费结算链路 ## 1. 已确认的供应商能力边界 本方案按以下已确认条件设计: - 同一个供应商账号支持建立多条 CMPP 长连接; - 单通道最多支持 8 条连接; - 单连接滑动窗口最大支持 64; - 连接数和窗口上限表示技术许可,不等于供应商账号承诺 TPS; - 实际配置仍必须服从账号总 TPS、通道限速、供应商网关稳定性和生产合同限制。 CMPP 规范建议窗口值为 16;本项目允许使用 32/64,是基于供应商明确支持的扩展能力。禁止未经阶梯验证直接启用 `8 连接 × 64 窗口`。 单通道理论最大在途 Submit 数量为: ```text 连接数 × 单连接窗口 = 8 × 64 = 512 ``` 512 是未收到 SubmitResp 时允许并行等待的最大请求数,不代表可以稳定达到 512 TPS。稳定 TPS 还取决于 SubmitResp 延迟和供应商账号限速,例如平均响应时间为 200ms 时,窗口理论容量不是首要限制;如果平均响应时间接近 1 秒,窗口容量才会直接限制吞吐。 ## 2. 改造目标与边界 目标: 1. 逐步提高每个供应商通道可承载的并发 Submit 数量; 2. 在单 Gateway 内完成多连接和窗口扩容,多个 Gateway 水平扩展降为 P2 后续可选项; 3. 将协议日志从发送和回调热路径移出; 4. 将逐事件 HTTP 回调改为可重放、逐事件幂等的批量回调; 5. 在扩容过程中保持消息、Submit、回执和计费不重不漏; 6. 使用非零单价验证真实计费并发,不以零计费压测结果作为容量依据。 本阶段不改变: - 对外 CMPP 协议和企业账号; - 签名、模板、余额、频控和企业/应用状态拦截语义; - 通道组主备补发规则; - `messageId`、`submitId`、结果事件和计费幂等口径; - PostgreSQL Submit Outbox 作为 Gateway 命令事实来源的设计。 ## 3. 改造一:单通道多连接与窗口扩容 ### 3.1 连接池模型 每个启用通道维护独立连接池,最多创建 8 条连接。每条连接独立维护: - `connectionId` 和 Gateway 实例标识; - CMPP CONNECT/CONNECT_RESP 认证状态; - 独立递增并循环使用的 SequenceId; - 当前未确认 Submit 数量; - `sequenceId -> submitId` 在途映射; - 当前窗口大小和剩余窗口; - SubmitResp 延迟、超时和连续失败次数; - ACTIVE_TEST 心跳、断链和重连状态; - 平滑启用、排空和关闭状态。 连接选择采用“可用窗口优先 + 最少在途 + 健康度”策略: 1. 排除未认证、正在重连、正在排空的连接; 2. 排除窗口已满的连接; 3. 优先选择未确认请求最少的连接; 4. 数量相同时优先选择近期 SubmitResp P95 更低的连接; 5. 连续超时连接进入冷却期,不再分配新 Submit。 ### 3.2 动态配置 建议新增或明确以下配置: ```text desiredConnections: 1..8 windowSize: 1..64 connectionWarmupSeconds: 30 connectionDrainTimeoutSeconds: 60 submitResponseTimeoutSeconds: 60 connectionFailureCooldownSeconds: 30 ``` 配置校验必须同时存在于管理 API 和 Gateway: - `desiredConnections < 1` 或 `> 8`:拒绝保存/拒绝启动; - `windowSize < 1` 或 `> 64`:拒绝保存/拒绝启动; - 扩容时逐条建立连接,不能同时突发 8 次登录; - 缩容时先停止向目标连接分配新 Submit,等待在途请求完成后断开; - 修改窗口时只影响新请求,不能丢弃已有在途映射。 ### 3.3 阶梯扩容参数 | 阶段 | 每通道连接 | 单连接窗口 | 理论在途/通道 | 用途 | |---|---:|---:|---:|---| | M0 | 1 | 16 | 16 | 当前兼容基线 | | M1 | 2 | 16 | 32 | 优先验证多连接正确性 | | M2 | 2 | 32 | 64 | 验证窗口扩容 | | M3 | 4 | 32 | 128 | 中等容量目标 | | M4 | 4 | 64 | 256 | 高容量目标 | | M5 | 8 | 64 | 512 | 上限验证,默认不直接用于生产 | 每一级必须单独通过正价 smoke、阶梯压测、故障注入和队列排空后才能进入下一级。 ### 3.4 异常与幂等处理 - 收到 SubmitResp 后必须先用连接和 SequenceId 找到稳定 `submitId`,再释放窗口; - 未找到在途映射的迟到 SubmitResp 进入异常对账,不能静默丢弃; - TCP 写入前失败可安全重新分配连接; - TCP 写入成功但未收到 SubmitResp时标记 `unknown`,禁止直接补发; - 连接断开后只处理该连接的在途请求,不能把整个通道的消息统一重发; - `submitId` 必须跨连接、跨实例保持唯一; - 主备通道补发必须使用新的 `submitId`,并通过 `retryOfSubmitRecordId` 关联原提交。 ## 4. P2 后续项:Gateway 水平扩展(当前不实施) 多个 Gateway 会引入通道连接所有权、分布式连接配额、租约、fencing token、脑裂、跨实例 pending 接管和“已写入供应商但未收到响应”的 unknown 判定,改造风险明显高于单 Gateway 内扩容。 因此本项调整为**优先级 P2 的后续可选改造**: - 不纳入当前开发和压测范围; - 不作为本轮达到目标 TPS 的前置条件; - 当前先使用单 Gateway 管理同账号多连接,单通道仍遵守最多 8 条连接、单连接窗口最多 64; - 只有单 Gateway 的 CPU、网络、文件描述符或事件循环成为经验证的主要瓶颈,且单实例优化无法继续提升时,才重新评审本项; - 未完成独立设计评审、故障模型验证和隔离环境演练前,不得直接部署双 Gateway。 以下内容仅作为 P2 预研设计保留,不代表当前实施计划。 ### 4.1 部署模型 部署至少两个 Gateway 实例,每个实例具有唯一的: - `gatewayInstanceId`; - Redis Stream Consumer Name; - 指标端口和健康检查地址; - 通道连接所有权和连接标识。 所有实例加入同一个 Submit Stream Consumer Group,共享: - Submit 命令队列; - 通道全局 TPS 限流; - Submit 发布和处理幂等键; - 结果/回执 Outbox; - 通道启停配置版本; - 待恢复和死信状态。 ### 4.2 推荐的首期分片方式 如果以后启动 P2,首期采用“通道分片 + 故障接管”: - 正常状态下,一个通道只由一个 Gateway 实例持有连接; - 不同运营商、主通道和备通道分散到不同实例; - 实例失联后,备用实例经过租约超时再接管通道; - 同一个通道不能被两个实例无租约地同时建立全部连接。 后续如果需要同账号跨实例并发连接,必须增加分布式连接配额:同一通道所有 Gateway 实例的连接总数不得超过 8。 ### 4.3 接管与脑裂保护 - Redis 或 PostgreSQL 中保存带过期时间的通道所有权租约; - 租约包含 `channelId`、`gatewayInstanceId`、配置版本和 fencing token; - 只有持有最新 fencing token 的实例可以领取该通道的新 Submit; - 旧实例恢复后必须先重新竞选租约,不能直接恢复发送; - Stream 未确认消息通过 `XAUTOCLAIM` 接管; - 对“可能已写入供应商 TCP”的请求不得盲目重发,应进入 unknown 对账。 ### 4.4 负载均衡边界 Gateway 水平扩展解决进程 CPU、网络、连接和单机故障问题,但不能突破: - 单通道最多 8 条连接; - 单连接窗口最多 64; - 供应商账号总 TPS; - PostgreSQL、Redis和回调服务的实际容量。 ## 5. 改造三:协议日志降载 ### 5.1 日志分级 | 日志等级 | 示例 | 处理方式 | |---|---|---| | 必留异常 | 拒绝、超时、断链、认证失败、重复/未知响应 | 100%保留,异步优先写入 | | 业务摘要 | Submit、SubmitResp、回执、上行、下行应答 | 异步批量写入 | | 原始协议包 | 完整请求/响应字段或二进制包 | 正常流量采样,异常全量短期保存 | ### 5.2 实现方案 - Gateway 不在 Submit/SubmitResp 热路径同步写协议日志数据库; - 日志事件写入独立 Redis Stream; - 独立日志 Worker 每批领取 100~500 条; - 使用批量插入和批量状态回写; - 正常成功事件按通道配置 1%~10% 采样; - 错误、超时、拒绝和断链事件保持 100%; - 原始包写滚动文件或对象存储,主库只保存检索摘要和对象地址; - 正常原始包建议保留 3~7 天,异常日志按审计策略保留; - Redis 日志 Stream 设置最大长度和积压告警。 日志积压不得阻塞 Submit。积压超过门槛时允许降低正常成功日志采样率,但禁止丢弃错误、安全和账务相关事件。 ### 5.3 必须保留的追踪字段 - `messageId`、`submitId`、`eventId`; - `channelId`、`gatewayInstanceId`、`connectionId`; - SequenceId 和供应商 Msg_Id; - 事件类型、方向、结果码和耗时; - 重试关联和原始日志对象地址。 ## 6. 改造四:Gateway 批量回调协议 ### 6.1 接口设计 新增内部接口: ```text POST /api/gateway/events/batch ``` 批次请求包含: - `batchId`; - `gatewayInstanceId`; - `createdAt`; - 1~100 个事件; - 每个事件独立的 `eventId`、`type` 和 `payload`。 首期支持事件类型: - `submit_segment_result`; - `submit_result`; - `receipt_intake`; - `uplink`; - `dead_letter`。 计费不由 Gateway直接生成。计费由 Submit结果/回执状态机依据稳定业务键执行,批量回调只负责可靠传递触发事实。 ### 6.2 成批策略 采用“数量或时间先到先发”: - 默认最大 50 个事件; - 可配置上限 100; - 默认最大等待 10ms; - 最大请求体 1MB; - Submit结果优先级高于普通协议日志; - 回执和上行可以使用独立批次,避免不同慢操作互相阻塞。 ### 6.3 响应与重试 API 必须返回逐事件结果,而不是只有整批成功或失败: ```json { "batchId": "CB-001", "results": [ { "eventId": "EV-001", "accepted": true }, { "eventId": "EV-002", "accepted": false, "retryable": true, "errorCode": "DB_BUSY" } ] } ``` - Gateway 只 ACK `accepted=true` 的事件; - 可重试失败使用指数退避; - 不可重试失败进入死信并告警; - HTTP 超时后允许重发整个批次,API 依靠 `eventId` 幂等; - API 可以按事件类型及每 20~50 条拆成短事务; - 单个坏事件不能造成整批回滚。 ### 6.4 数据库批量处理 - `eventId` 建唯一索引或使用既有稳定幂等键; - Submit结果批量更新 `SmsSubmitRecord`; - 回执先批量写入耐久 Receipt Inbox,再异步完成聚合; - 计费使用稳定计费键批量创建,重复事件不得重复扣费; - 消息和任务进度使用集合更新,避免逐事件 `GROUP BY`; - 事务中禁止调用外部 HTTP、Redis或供应商接口。 ### 6.5 兼容与回退 - 保留原单事件回调接口; - 使用开关选择单事件或批量接口,同一事件不能双发; - 新旧入口共享 `eventId` 幂等逻辑; - 回退只切换发送方式,不删除回调 Outbox 或幂等记录; - 回退前必须等待在途批次完成并核对 Redis PEL。 ## 7. 当前实施顺序与 P2 后续项 ### 当前步骤 1:可观测性和协议日志降载 先补齐连接、窗口、供应商响应延迟和日志积压指标,再将成功协议日志异步批量化。该阶段不提高连接数。 ### 当前步骤 2:单实例多连接 实现连接池、窗口隔离、平滑扩缩容和断链恢复,依次验证 `2×16`、`2×32`、`4×32`。 ### 当前步骤 3:批量回调协议 上线批量接口和逐事件幂等,先影子对账,再切换 Submit结果,最后切换回执和上行。 ### 当前步骤 4:单 Gateway 容量上限验证 只有前述阶段稳定后,才在单 Gateway 内测试 `4×64`、`8×64`。生产建议值依据压测结果确定,不以供应商允许的最大参数直接作为生产配置。 ### 优先级 P2:多 Gateway 水平扩展 当前不实施、不部署、不纳入本轮压测。将来只有证据表明单 Gateway 自身成为主要瓶颈时,才单独立项,执行通道分片、租约防脑裂、故障接管和 unknown 对账验证。 ## 8. 正价压测方案 ### 8.1 前置条件 - 仅在隔离测试环境执行; - 压测前完成数据库、环境文件、部署目录和 systemd 配置备份; - 测试应用单价设置为非零值,并记录压测前余额; - 使用可明确识别的移动、联通、电信混合号码; - 主备六通道均处于预期状态; - Submit、结果、回执、日志 Stream 和数据库 Outbox 初始无积压; - 压测前记录 Gateway 实例、连接数、窗口和供应商限速配置。 ### 8.2 阶梯 每个连接/窗口档位分别执行: 1. 9 条正价全链路 smoke; 2. 20 TPS; 3. 30 TPS; 4. 50 TPS; 5. 70 TPS; 6. 100 TPS; 7. 达标且无停止条件时再测试 150/200 TPS。 每档结束后等待完整排空并对账,不能只检查入口 SubmitResp。 ### 8.3 验收指标 - 客户 SubmitResp 接收数和唯一 MessageId 数; - Inbox 完成数; - 首次供应商 Submit 数和每秒吞吐; - 各通道、各连接的提交数和在途峰值; - SubmitResp P50/P95/P99; - 供应商接受、拒绝、超时和 unknown 数; - 主备补发关联完整性; - 最终 delivered/failed/submitted/unknown 数; - 回调批次大小、请求数、重试数和积压; - 数据库连接池等待、锁等待和慢 SQL; - 协议日志 Stream 积压和丢弃/采样计数; - 计费记录数、计费单位、扣费金额与余额变化。 正价计费恒等式至少满足: ```text 压测前余额 - 压测后余额 = 有效计费记录金额合计 = 各消息 billingUnits × 应用单价之和 ``` ### 8.4 停止条件 出现任一情况立即停止升档: - SubmitResp 缺失或 MessageId 重复; - `submitId` 重复提交; - 重复计费、漏计费或余额不平; - 供应商超时/unknown 持续增加; - Redis Stream、Inbox、Outbox或回调持续积压且无法在规定时间排空; - 数据库连接等待、锁等待或 idle transaction 异常; - Gateway 频繁断链或连接数不能恢复; - 通道禁用、主备补发或发送拦截语义异常; - 日志降载导致错误、安全或账务事件丢失。 ## 9. 故障回归 必须覆盖: - 单连接断开,其余连接继续发送; - 同一通道从 4 条连接缩到 2 条连接,在途请求不丢失; - 单 Gateway 进程重启后恢复连接、在途状态和 Stream pending; - Redis 短暂不可用; - 批量回调 HTTP 超时后整批重放; - 回调批次中单个非法事件不拖累其他事件; - PostgreSQL 回调连接池耗尽时不阻塞 Submit Outbox; - 主通道 Submit拒绝触发备通道关联补发; - 主通道禁用后只使用备通道; - 主备同时禁用时发送在供应商前失败; - 签名/模板未报备、余额不足、企业/应用删除或禁用、号码频控仍能正确拦截; - 重启和事件重放不造成重复提交、重复回执或重复计费。 多 Gateway 的实例宕机接管、租约、fencing token 和脑裂测试属于 P2 后续项目,不纳入当前故障回归范围。 ## 10. 容量预期 容量提升不能简单相加。最终吞吐由最慢环节决定: ```text 实际 TPS = min( 入口能力, Worker/Outbox能力, Gateway连接与窗口能力, 供应商账号总TPS, 回调与计费能力, PostgreSQL/Redis能力 ) ``` 以最近一次测试记录的完整供应商首提约 33 TPS 为历史参考,执行前必须重新验证当前基线。工程预期区间如下: | 完成范围 | 预期稳定首提吞吐 | |---|---:| | 独立回调 + 批量路由/持久化 | 50~70 TPS | | 加协议日志降载 + `2×16`/`2×32` | 60~100 TPS | | 加 `4×32` 或 `4×64` | 80~150 TPS | | 单 Gateway `8×64` 上限验证 | 由供应商总TPS及单机实测决定,不预先承诺 | | 多 Gateway 水平扩展(P2) | 当前不实施,另行立项后评估 | | 300~500 TPS | 不作为当前承诺;未来可能需要供应商总TPS、通道数量和基础设施共同扩展 | 上述数字是容量规划目标,不是承诺值。即使单通道理论支持 512 个在途请求,如果供应商账号总限速为 50 TPS,平台稳定吞吐仍不会超过约 50 TPS。 ## 11. 生产推荐原则 - 生产参数使用“最低但足够”的连接数和窗口,不长期顶格运行; - 初始推荐从 `2 连接 × 16 窗口` 开始; - 只有监控证明窗口经常耗尽且供应商响应稳定,才提高到 32/64; - 当前不增加 Gateway 实例;只有单实例 CPU、网络、文件描述符或事件循环成为经验证且无法继续优化的瓶颈,才启动 P2 水平扩展评审; - 保留连接数、窗口、批量回调、日志采样和 Gateway分片的独立回退开关; - 每次只改变一个主要容量变量,保留可比较的正价压测基线。 ## 12. 2026-08-25 实施与测试结论 本轮已实施本方案中除“多 Gateway 水平扩展(P2)”外的改造,并仅发布到测试环境 `100.93.204.60`。发布前恢复点为 `/opt/cmpp-platform-backups/phase5-capacity-20260825T041622Z`,数据库、运行源码和 systemd/环境配置归档均已校验;预生产环境未操作。 已交付能力: - Gateway 协议摘要日志改为 Redis Stream 异步写入,成功事件确定性采样 10%,拒绝、超时、断链和非零结果 100% 保留;独立 `cmpp-protocol-log-worker` 使用独立 4 槽 PostgreSQL 连接池批量落库,成功后 ACK/XDEL,失败重放,格式错误进入死信 Stream。 - 单通道支持 1~8 条供应商连接、每连接 1~64 窗口;连接选择按在途数量和近期 RTT,连续失败进入冷却。扩容串行预热;缩容先停止分配、等待在途结束或排水超时后关闭。 - Gateway 结果 Outbox 支持最多 50 条、最长等待 10ms 的批量 HTTP 回调;Submit 结果、分片结果、回执、上行和死信均返回逐事件结果,整批 HTTP 失败留在 PEL 重放,单个非法事件不拖累同批其他事件。 - API、Gateway 控制入口和实际运行状态完成三层校验。运行矩阵覆盖 `1x1`、`2x16`、`4x32`、`8x64`、`4x64`、`2x32`,越界 `0/9` 连接及 `0/65` 窗口均被拒绝。 - 上行增加 `eventId` 唯一幂等键;同一批量上行事件重放两次只生成一条 `SmsUplinkMessage`。 发布后曾发现协议日志发布器在仅配置 `REDIS_HOST/REDIS_PORT` 的环境中没有对空 `REDIS_URL` 回退。该问题触发停止条件,未进入阶梯;修正为与其他 Gateway Redis 组件一致的本机回退、增加测试并重发后,冒烟显示发布 16、采样丢弃 47、错误 0,日志 Stream 最终 `pending=0/lag=0`。 ### 12.1 正价阶梯结果 测试单价为 325 分/计费条,三运营商混合号码,六个模拟供应商账号;压测连接配置为每通道 `2x32`。容量按非补发的首次供应商 Submit 统计。 | 目标档 | 客户 SubmitResp | 首次供应商 Submit | 首提跨度/实际速率 | 客户 P50/P95/P99 | 计费 | 结论 | |---:|---:|---:|---:|---:|---:|---| | smoke | 9/9 | 9 | 约 8.1 秒/低速冒烟 | 17/1727/1727 ms | 9 笔/2925 | 冷启动尾延迟后链路闭合 | | 20 TPS | 199/199 | 199 | 约 9.8 秒/约 20 TPS | 14/21/29 ms | 199 笔/64675 | 通过 | | 30 TPS | 299/299 | 299 | 9.840 秒/30.39 TPS | 16/33/61 ms | 299 笔/97175 | 通过 | | 50 TPS | 499/499 | 499 | 10.086 秒/49.47 TPS | 22/62/89 ms | 499 笔/162175 | 通过 | | 70 TPS | 699/699 | 690 | 10.351 秒/66.66 TPS | 94/4099/5323 ms | 690 笔/224250 | 触发停止线,不再升档 | | 100/150/200 TPS | 未执行 | - | - | - | - | 按停止条件立即停止 | 70 TPS 档少 9 条首次 Submit 和计费并非丢失:9 条均为重复使用的测试号码命中“单号码 24 小时最多 10 条”,最终 `failed/RISK`,供应商提交和计费均为 0。该结果证明频控拦截有效,但 70 TPS 的 P95/P99 已从 50 档的 `62/89ms` 跃升到 `4099/5323ms`,目标 70 TPS 也只完成 66.66 TPS,因此本版本稳定上限定为 **50 TPS**;70 TPS 只能视作非稳定冲击档。 ### 12.2 完整回归与恢复 - Submit/回执:各通过档客户 SubmitResp 无缺失,MessageId、submitId 和供应商 MessageId 无重复;模拟器 DELIVRD/失败/无回执比例按配置落库,三条 Stream 最终均 `pending=0/lag=0`。 - 计费:20/30/50 档及 70 档未被风控拒绝的消息,计费笔数均等于有效消息数,单价全部 325,金额满足 `条数 x 325`;风控、余额不足和路由失败不计费。 - 主备:随机结果码 8 产生带 `retryOfSubmitRecordId` 的备通道补发;仅主通道停用时 9 条全部走 `LGST-M-B`,主备六通道全停时 9/9 `failed/ROUTE`、供应商提交 0。 - 上行/批量回调:有效上行与非法事件同批时互不影响,非法项为不可重试;同一 eventId 重放两次仅落一条上行记录。 - 拦截:签名未审核、模板强制匹配但未审核、余额不足分别为 9/9 `SIGNATURE/TEMPLATE/BALANCE` 且供应商提交 0;应用或企业停用时客户连接不能鉴权;频控在 70 档真实命中;所有临时状态均恢复。 - 平滑扩缩容:`LGST-M-P` 从 1 扩到 4,再在 20 TPS、20 秒发送期间缩到 1;399/399 SubmitResp,399 条首次 Submit,无重复 ID,P95/P99=`49/142ms`,最终连接恢复 6/6。 - 故障:Gateway 重启后由 API 恢复连接;Redis 在队列排空时短暂停止,Gateway 健康进程保持,Redis 恢复后重启 Gateway/API 并恢复 6/6;整批 HTTP 超时重放和单事件隔离另有自动化测试覆盖。 - 环境恢复:10 个测试应用单价恢复 0,企业/应用/签名/模板/通道均恢复原状态,三条临时号段规则删除,六通道连接恢复 6/6。真实消息、回执和账务事实保留审计,不回写余额。 ## 13. 2026-08-25 回执回放风暴修复与正价复压 旧结论中70 TPS的4秒级尾延迟不是前几轮Worker、Outbox或计费优化失效,而是Gateway在每条SubmitResp后并发扫描同账号pending回执;API仅查询pending,直到异步sent回调才改状态,因此多个扫描会读到同一批。旧版100/150冲击档客户回执重复曾达847/5101条,Gateway goroutine在150档峰值超过1.2万。 修复内容: - Gateway同账号flushPending单飞,SubmitResp触发改为25ms尾随合并、250ms最长等待,登录和周期恢复保留; - API使用FOR UPDATE SKIP LOCKED原子将pending领取为dispatching,绑定claimId和5~120秒可控租约;当前Gateway默认30秒; - 客户离线或SubmitResp屏障时用claim_released无损释放,不消耗业务重试预算;过期dispatching自动恢复为pending; - API新建回执后的直推也必须先原子取得dispatching + claimId,从根源消除直推与Gateway恢复各发一次的竞态; - ACK超时定时器在ACK registry锁内完成指针赋值,Linux竞态检查不再报告该竞态。 发布仅到测试机100.93.204.60,预生产未操作。恢复点为/opt/cmpp-platform-backups/phase5-replay-hotfix-20260825T070815Z,PostgreSQL custom dump、运行源码、systemd/环境配置和SHA-256均已校验。Linux go test -race ./internal/inbound、Gateway全包测试/go vet、API 44套520项和TypeScript构建通过。最终运行标记为761c123b65f09093bc379096188f3ee9ccb2e618+workspace.phase5.replayhotfix2.directclaim。 正价阶梯(单价325): | 目标档 | 客户SubmitResp | 客户P50/P95/P99 | 首次供应商Submit跨度/速率 | 结论 | |---:|---:|---:|---:|---| | 20 | 199/199 | 5/10/16ms | 9.930s / 19.84 TPS | 通过 | | 30 | 299/299 | 5/10/18ms | 9.854s / 30.34 TPS | 通过 | | 50 | 499/499 | 7/35/47ms | 10.067s / 49.47 TPS | 通过 | | 70 | 699/699 | 14/44/60ms | 10.666s / 65.54 TPS | 入口稳定,首提接近70档 | | 100 | 999/999 | 24/51/72ms | 13.345s / 74.86 TPS | 入口通过,端到端未达100 | | 150 | 1498/1498 | 49/87/114ms | 15.781s / 94.86 TPS | 冲击档,显示端到端天花板 | | 200 | 1998/1998 | 50/84/110ms | 21.661s / 92.24 TPS | 冲击档,端到端不再增长 | 直推领取修复后另执行100 TPS正价复验:999/999,P50/P95/P99=54/102/179ms;999个唯一MessageId、999次非补发首提在12.488秒完成(80.00 TPS)。972条已产生最终回执的投递全部ACK,生命周期尝试972、重复0、最大尝试1。计费998条:995 charged、3条供应商最终失败后refunded;1条RISK在供应商前失败且未计费。27条模拟器no-receipt保持submitted,不是队列丢失。Inbox、Submit Outbox、下游pending/dispatching/awaiting_ack、未授权锁和idle transaction最终均为0。 最终结论分两种口径:客户入口SubmitResp受理已通过200 TPS冲击;完整供应商首提天花板约95 TPS。测试环境建议稳定限速70 TPS,保留约25%以上端到端余量;不将入口200 TPS宣称为供应商200 TPS。按用户要求,7个测试应用单价保持325,^1380028/^1300028/^1890028三条临时号段规则保留,原100.91.249.119/32和新增127.0.0.1/32白名单均保留,未做测试后删除或归零。 ## 14. 95 TPS天花板定向诊断 100/150 TPS正价复验确认,Gateway的6条连接和192个窗口没有饱和;150档连接等待仅0.006ms、供应商RTT约13.761ms、窗口峰值10,命令Stream lag峰值27。真正的平台内排队发生在耐久Inbox之后、BullMQ发送之前的独立CMPP入站工作流。 当前7个压测应用全部归属同一企业。尽管工作流配置为并发96、批次64,泵在一批完成后立即领取当时已有记录,100/150档实际平均批次仅5.84/9.79条,导致同一企业的日限、号码频控、消息持久化和正价账户冻结被大量小事务重复执行。150档Inbox完成延迟P50/P95为3.779/5.620秒,1498条首次供应商Submit用15.547秒完成,即约96.35 TPS;因此当前约95 TPS应定义为“单企业正价完整首提上限”,不能定义为多企业平台总上限。 下一阶段优先级: 1. 为入站工作流增加按企业分组的有界微批,目标批次32~64、最大等待20~50ms; 2. 不同企业允许并行,同企业账户冻结保持串行和幂等; 3. 继续合并配额、频控、消息及冻结流水的集合事务; 4. 分别验证单企业100/150 TPS,以及至少两个独立企业合计200 TPS; 5. 只有单Gateway资源被实测打满后才启动多Gateway P2。 诊断还发现Outbox原生SQL使用`CURRENT_TIMESTAMP`,与Prisma UTC时间混用时会产生8小时审计偏差。该问题不影响本次吞吐和队列排空结论,但必须在下一次代码改造中统一为UTC并回归租约、重试和发布时延。 ## 15. 单企业工作流微批治理实施范围 本轮只在单Gateway架构内处理95 TPS定向诊断暴露的工作流问题,不实施多Gateway P2。具体改造为:领取前最多等待40ms把小批合并到目标32条;按企业分组,不同企业并行、同企业各批串行;领取SQL排除正在执行的企业;Submit Outbox及关联消息的原生SQL时间统一为UTC。环境参数必须显式配置并由发布脚本校验,目标批量不得超过实际批次上限。 发布验收保持应用单价325以及现有临时号段规则和白名单。先做单企业100/150 TPS,以确认原约95 TPS边界是否改善;再使用至少两个独立企业做聚合200 TPS,用于区分单企业账户一致性边界和平台总吞吐。所有档位都按非补发首次供应商Submit计算,并对账Submit、回执、上行、账务、主备补发、拦截规则与队列排空;停止条件沿用第五阶段,不以入口SubmitResp替代完整链路结论。