webman单机无法支撑百万级长连接,实测上限仅3–5万,需通过连接网关分离、读扩散+消息中心、redis分片状态管理、消息落盘持久化等分层架构改造,使其退居为轻量协议翻译层。

Webman 本身不是为百万级长连接设计的框架,直接用它扛单群百万在线,会卡死在连接层和消息广播上。必须拆解瓶颈点,用分层架构补足短板。
Webman 的 WebSocket 连接能力有硬上限
Webman 基于 Swoole,默认使用 swoole_websocket_server,单进程连接数受 PHP 内存、Linux 文件描述符(ulimit -n)、Swoole 协程栈大小共同限制。实测中,单机稳定维持 3–5 万并发 WebSocket 连接已是极限,超量会导致 Connection reset by peer 或协程调度延迟飙升。
- 必须做连接网关分离:Webman 只负责业务逻辑与协议编解码,把连接管理下沉到专用网关(如基于 T-IO、Netty 或自研 epoll 网关)
- 若坚持全栈 Webman,需横向扩到至少 20+ 实例,并前置 LVS/DPDK 负载均衡,且每个实例绑定独立端口避免
TIME_WAIT拥塞 -
worker_num不宜设过高(建议 ≤ CPU 核数 × 2),否则协程切换开销反噬吞吐;重点调优max_coroutine和buffer_output_size
群消息广播不能靠 Webman 原生 foreach 推送
单群百万成员时,一条消息若在 Webman 里遍历所有 session 调用 $session->send(),仅序列化+拷贝就吃光内存,更别说网络 write 阻塞导致整个 worker 卡住。这不是性能问题,是架构错误。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 必须切换为「读扩散 + 消息中心」模式:消息只写一次到共享存储(如 Redis Streams 或 Kafka),客户端上线/拉取时按游标消费
- 若仍需实时推送,Webman 应只推送给「在线状态服务」,由该服务异步查出目标用户所在网关节点,再走内部 RPC 或 Pub/Sub 下发
- 禁用
server->push()全局广播;改用server->getClientInfo()结合分片路由,每次最多触达 1 万个 session
未读数与状态同步必须脱离 Webman 主循环
Webman 的事件循环不擅长高频小包状态更新。每用户心跳、在线/离线标记、未读数累加若都在同一个进程处理,onMessage 和 onClose 会互相阻塞,导致消息积压。
- 在线状态用 Redis Hash 分片存储(
online:shard_001),用HSET/HDEL原子操作,避免锁 - 未读数改用 Redis Sorted Set 存储:key 为
unread:{group_id}:{user_id},score 为最后已读msg_id,新消息到来时ZCOUNT计算差值 - 心跳检测剥离为独立定时器协程,每 30 秒扫一次过期连接,不混入业务 handler
消息可靠性不能依赖 Webman 的内存队列
Webman 没有内置消息持久化机制。onMessage 中抛异常或进程崩溃,消息就丢了。百万级场景下,任何单点内存暂存都是风险源。
- 所有关键消息(尤其是群聊)必须第一时间落盘:写入 MySQL 分库分表 or ClickHouse(适合高吞吐写入) or RocketMQ Topic
- 重试队列必须用 Redis ZSet + Lua 脚本实现原子认领,避免多个 Webman 实例重复投递
- 客户端需携带
client_msg_id,服务端用SETNX做幂等校验,防止弱网重传造成重复
真正难的不是写多少行 onMessage 逻辑,而是让 Webman 不去干它不该干的事——连接管理、广播分发、状态聚合、消息兜底,这些都得交给更专业的组件。Webman 在这个架构里,只应是一个轻量、可控、易调试的「协议翻译层」。










