CMPP 服务端并发模型怎么设计

24 阅读3分钟

在做短信平台这些年,CMPP 服务端的性能瓶颈,80% 都不是协议本身,而是并发模型设计的问题。很多系统“能跑”,但一上量就开始抖:连接堆积、消息延迟、窗口阻塞,甚至直接把网关拖垮。

这篇就把 CMPP 服务端并发模型怎么设计讲清楚,偏工程实践。


一、先明确几个核心约束

CMPP 本质是一个长连接 + 请求响应 + 窗口控制的协议,几个关键点决定了并发模型:

  • 长连接(TCP) :一个客户端(SP)通常会建立多条链路
  • 滑动窗口(Window) :限制未响应消息数量(常见 16/32/64)
  • 双向通信:既要处理 Submit,也要下发 Deliver
  • 强时序依赖:SequenceId 必须匹配响应

👉 结论:这不是简单的 HTTP 模型,而是一个强状态 + 高并发 + 低延迟系统。


二、常见错误模型(踩坑总结)

很多团队一开始会这么设计:

1. 一连接一线程

  • 每个 TCP 连接绑定一个线程
  • 问题:连接数一上来(几千级),线程直接爆炸

2. 线程池 + 阻塞 IO

  • 用线程池处理请求
  • 问题:CMPP 是长连接 + 高频 IO,线程上下文切换成本极高

3. 收发不分离

  • 收消息、处理逻辑、发响应在同一线程
  • 问题:一旦业务处理慢,直接阻塞整个链路

👉 这些模型在 TPS 上到几千后就会明显掉队。


三、推荐并发模型:Reactor + 多级队列解耦

实际线上稳定跑高并发的方案,一般是这个结构:

1. 网络层:Reactor(NIO/epoll)

  • 使用 Netty / 自研 NIO

  • 单机支持数万连接没问题

  • 线程模型:

    • Boss线程:处理连接
    • Worker线程:处理读写事件

👉 核心原则:网络 IO 必须非阻塞


2. 协议解码层(轻量)

收到数据后只做三件事:

  • 粘包拆包
  • CMPP 报文解析
  • 投递到业务队列

⚠️ 注意:
这里绝对不能做业务逻辑


3. 业务处理层(核心并发控制)

采用 多级队列 + 线程池隔离

队列拆分建议:

  • Submit 队列
  • Deliver 队列
  • Report 队列
  • 心跳处理(独立)

为什么要拆?

因为:

  • Submit 是高频写操作
  • Deliver 是下行推送
  • Report 对时效要求更高

👉 混在一起一定互相拖慢


4. 窗口控制(关键中的关键)

CMPP 并发不是线程数,而是窗口数:

  • 每个连接维护:

    • window_size
    • unacked_count

发送时:

if (unacked_count < window_size) {
    send();
    unacked_count++;
}

收到响应:

unacked_count--;

👉 本质是一个信号量模型


5. 发送模型(异步化)

推荐:

  • 发送队列 + 批量刷写(write buffer)
  • 避免每条消息一次 syscall

优化点:

  • 合并 write(Netty flush 策略)
  • 减少锁竞争(无锁队列/MPSC)

四、进阶优化(决定上限)

1. 连接维度限流

  • 单 SP 连接数限制
  • 单连接 TPS 限制
  • 防止“劣质客户拖垮全局”

2. 内存池化

  • ByteBuf / DirectBuffer 复用
  • 避免频繁 GC

3. SequenceId 生成策略

  • 必须线程安全 + 高性能

  • 常见方案:

    • 原子递增(AtomicLong)
    • 分段 ID(减少竞争)

4. 超时重试机制

  • 未响应消息超时回收
  • 防止窗口被“卡死”

5. 多机水平扩展

  • 无状态设计(连接状态本地化)
  • 前置 LB(四层负载)
  • 按 SP / 账号分片

五、一套可落地架构总结

可以把整个并发模型抽象成:

[Netty Reactor][解码 & 协议层][多队列分发][业务线程池][发送队列 + 窗口控制][异步写回]

核心设计原则就三条:

  1. IO 与业务彻底解耦
  2. 所有阻塞点必须异步化
  3. 并发控制靠窗口,而不是线程

六、经验结论(很重要)

  • CMPP 的上限不是机器性能,而是窗口设计 + 调度能力

  • 真正稳定跑到高 TPS 的系统,一定是:

    • 事件驱动(Reactor)
    • 队列化(削峰)
    • 限流(保护系统)

如果你现在的系统已经出现:

  • 延迟抖动
  • submit_resp 超时
  • deliver 堵塞

基本可以确定:并发模型需要重构了,而不是简单扩容机器。