核心在于控制线程增长节奏、复用连接资源、及时释放底层socket,而非依赖线程池自动伸缩;应限制并发线程数、禁用无约束弹性线程池、显式设max_workers上限、改用拒绝策略降级、转向异步i/o(如netty/aiohttp)、主动管理socket生命周期并加强监控熔断。
核心在于控制线程增长节奏、复用连接资源、及时释放底层 socket,而不是依赖线程池自动伸缩来应对突发流量。
限制并发线程数量,关闭无约束扩容
弹性线程池(如 Java 的 ThreadPoolExecutor 配合 Executors.newCachedThreadPool() 或 Python 的 concurrent.futures.ThreadPoolExecutor(max_workers=None))在高并发推送场景下极易因任务堆积而无限创建线程。每个线程默认携带独立的栈空间(通常 1MB),叠加 TCP socket 缓冲区(SO_RCVBUF/SO_SNDBUF)后,物理内存消耗呈倍数放大。
- 显式设置
max_workers上限(例如 50~200,依据机器内存和平均连接生命周期评估) - 拒绝策略改用
threading.BoundedSemaphore或RejectedExecutionHandler抛异常/降级,而非排队等待 - 禁用“corePoolSize=0 + allowCoreThreadTimeOut”组合,避免空闲线程被回收后又立刻重建
用异步 I/O 替代线程池承载长连接
长连接推送本质是 I/O 密集型任务,线程模型天然低效。应转向事件驱动架构,让单线程或少量线程处理成千上万连接。
- Python 推荐使用
asyncio+aiohttp/websockets,避免requests同步调用混入协程 - Java 推荐 Netty,配合
EventLoopGroup管理 IO 线程,禁用DefaultEventExecutorGroup无节制派生 - 关键:所有 write 操作必须非阻塞,且绑定到连接生命周期——连接断开时立即 cancel 对应协程/ChannelFuture
主动管理 socket 缓冲区与连接生命周期
TCP 缓冲区内存不释放,往往不是线程没关,而是 socket 对象仍在内存中被引用,或内核缓冲区未清空。
- 启用
TCP_USER_TIMEOUT(Linux)或SO_KEEPALIVE+ 自定义心跳,快速发现并清理僵死连接 - 定期扫描并 dump 内存中的
socket.socket实例(Python)或SocketChannel(Java),确认无泄漏 - 发送端写入后检查
write_failed日志或ChannelInactive事件,触发close()和资源清理 - 对高频小包推送,合并为 batch 发送,减少系统调用和缓冲区碎片
监控与熔断双保险
仅靠编码约束不够,需运行时感知风险并干预。
- 暴露指标:活跃连接数、socket 文件描述符数、线程数、TCP 缓冲区总占用(
/proc/net/sockstat中used字段) - 当 socket 数 > 80% ulimit 或内存中 socket 实例 > 5000 时,触发自动降级(如暂停新连接接入、切至短连接兜底)
- 结合
py-spy dump --pid或jstack快速定位哪些线程卡在send()或recv(),是否因对端未读导致发送缓冲区积压











