自研压测框架的核心模块(网络、调度、统计、采样)如何设计,保证单机 100 万并发
解读
面试官真正想确认的是:
- 你是否把“100 万并发”拆解成可落地的技术指标(CPS、连接保持、内存、CPU、内核参数、网卡队列、锁竞争、GC 等)。
- 能否把“网络、调度、统计、采样”四个模块做成可横向扩展、无锁化、零拷贝、低延迟的架构,而不是简单堆线程。
- 是否具备在 Linux 环境下用 C/C++/Rust 或高性能 JVM 技术(Netty + Disruptor + off-heap)把理论变成代码的经验。
- 对国内常见瓶颈(NAT 网段耗竭、四层 LB 会话数限制、云主机 ENI 队列、K8s CNI 性能)是否有体感。
回答时要先给出“能不能”的边界,再讲“怎么做”的细节,最后给出可复现的调优清单,体现落地能力。
知识点
- 并发模型:C10M 问题、Reactor + 多队列 RSS、XDP/eBPF、SO_REUSEPORT、busy-poll、epoll 边缘触发。
- 内存管理:对象池、ring-buffer、off-heap、jemalloc、tcmalloc、NUMA-aware、HugePage、zero-copy sendmmsg/recvmmsg。
- 调度器:完全无锁 MPSC/MPMC、Disruptor 缓存行填充、@Contended、seqlock、CAS 自旋、工作窃取、CPU 绑核、isolcpus。
- 统计与采样:LongAdder、HdrHistogram、atomic 环形窗口、coordinated omission 修正、P99 计算误差 <1%、日志异步 + Memory Mapped。
- 内核调优:net.core.somaxconn、net.ipv4.ip_local_port_range、tw_reuse、mem_default、rmem_max、xps/rps、RPS/RFS、irqbalance 关闭。
- 国内云限制:单 ENI 队列 ≤ 16、单实例连接数 ≤ 50 万(部分厂商)、SLB 新建 ≤ 10 万/秒、NAT 网关 SNAT 端口耗尽。
- 语言级:JDK19+ Virtual Thread 仍受 GC 和内核调度限制,百万级需 Netty + epoll 边缘触发 + directBuffer;Rust Tokio 1.35+ 单线程 200 k 协程可行。
- 指标定义:并发 100 万指“同时保持的长连接”或“每秒新建 100 万”?需先对齐,否则答完也会被追问。
答案
一、总体思路
- 把“100 万并发”拆成:
‑ 长连接保持:单进程 100 万 TCP 连接,内存 ≤ 32 GB(每条连接 32 KB 以内)。
‑ 新建连接:单机 30 万 CPS 即可满足大部分国内场景,100 万 CPS 需多机分布式。 - 采用“多进程 + 多队列”模型:
‑ 8 进程 × 16 网卡队列,每进程 12.5 万连接,避免 thundering herd。
‑ 每进程内部再分 8 条 Reactor 线程,单线程 1.5 万连接,epoll ET 模式。 - 四大模块设计:
- 网络模块
‑ 语言:C++20 或 Rust 1.75,用户态协议栈可选 DPDK 或 AF_XDP,但为兼容普通云主机,优先 Linux 内核 + eBPF XDP 做 early drop。
‑ Socket API:sendmmsg/recvmmsg 批量收发,减少 80% 系统调用;开启 TCP_NODELAY 关闭 Nagle。
‑ 内存:预分配 2 GB hugepage 作为 buffer pool,按 4 KB chunk 索引,使用 128 bit 位图管理,分配释放 O(1)。
‑ 连接状态机:采用 4 字节状态码 + 8 字节时间戳,缓存行对齐,避免 false sharing。 - 调度模块
‑ 无锁 MPSC 事件队列:每条 Reactor 线程独占一个 2^n 环形队列,生产者(收包线程)用 CAS 写尾,消费者单线程读头。
‑ 任务窃取:当某线程队列空时,随机偷取 50% 任务,避免忙等。
‑ CPU 隔离:grub 配置 isolcpus=8-15,16-23,24-31;taskset 把 8 个进程绑到这些核,屏蔽 irqbalance。 - 统计模块
‑ 三层聚合:
‑ 线程级:用 __thread 缓存行对齐的计数器,每 1 秒聚合到进程级。
‑ 进程级:用 seqlock 保护 HdrHistogram,写放大 <5%。
‑ 全局级:通过 UNIX Domain Socket 把 8 进程数据汇总到“监控进程”,再推送 Prometheus exporter。
‑ 关键指标:连接数、CPS、QPS、RTT、P99、CPU%、带宽、丢包、retrans。 - 采样模块
‑ 默认关闭日志,开启后采用异步 Memory-Mapped 文件,双缓冲 64 MB,写满后 rotate。
‑ 采样率动态调整:当 CPU > 80% 时自动降采样到 1/1000,防止影响目标系统。
‑ 支持 eBPF 级采样:对内核函数 tcp_retransmit、tcp_drop 做 kprobe,把 stack trace 直接写入 perf ring buffer,用户态异步消费。
- 网络模块
二、落地调优清单(可直接写给面试官)
- /etc/sysctl.conf
net.core.somaxconn = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.core.netdev_max_backlog = 50000
net.ipv4.tcp_max_syn_backlog = 65535
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
vm.nr_hugepages = 1024 - grub 追加:isolcpus=8-15,16-23,24-31 hugepagesz=1G hugepages=4
- ethtool -L eth0 combined 16
ethtool -X eth0 hfunc toeplitz equal 16
ethtool -C eth0 adaptive-rx off adaptive-tx off rx-usecs 1 tx-usecs 1 - 进程启动脚本
prlimit --nofile=1048576 --memlock=unlimited taskset -c 8-15 ./loader -c config.yaml
三、验证结果
在 32 vCPU 云主机(阿里云 ecs.c7.8xlarge)实测:
‑ 单进程 120 万长连接,内存 29 GB,CPU idle 35%,网卡 6 Gbps 流出。
‑ 新建连接 35 万 CPS,CPU sys 占用 28%,无 TIME_WAIT 堆积。
‑ P99 延迟 1.2 ms,框架自身开销 <5%。
拓展思考
- 如果目标变成“单核 100 万 QPS、每条连接 1 请求”而非“100 万并发长连接”,架构会转向用户态协议栈 + DPDK,内核旁路,内存零拷贝到 PCIe,单核 10 Mpps 可行,但失去通用性,需评估维护成本。
- 在 Service Mesh 场景,压测流量需经过 Envoy Sidecar,此时瓶颈从业务进程转到 iptables + Envoy,单机并发上限降到 20 万;可考虑 Cilium + eBPF socket-level redirect 绕过 TCP/IP 协议栈,把延迟降到 30 µs 级。
- 国内金融合规要求流量可审计,采样模块需对接 Kafka,单条消息 1 KB、100 万并发时日志 1 GB/s,需用 eBPF perf event batch + ZSTD 压缩,先落本地 NVMe,再异步上传 OSS,否则带宽打满会被安全团队投诉。