workerman 4.0.34通过常驻内存、多进程+事件驱动非阻塞i/o模型,绕开php-fpm生命周期限制,支持单机数万级长连接;需配置合理进程数、启用内置心跳或手动保活、在onworkerstart初始化资源、onconnect/onmessage/onclose规范管理连接与数据,并采用异步db或消息队列避免阻塞。

Workerman 4.0.34 高并发长连接开发指南
要在单机上稳定支撑数万级长连接并保持低延迟响应,必须绕开PHP-FPM传统模型的生命周期限制,让每个Worker进程独立持有健康连接、避免共享资源竞争,同时确保I/O操作全程非阻塞。这要求从进程配置、协议选择、连接管理到异常处理全部按事件驱动逻辑重构。
初始化 Worker 实例并设置多进程模型
创建 server.php 文件,引入 Autoloader 并声明 WebSocket 协议监听实例:
require_once 'workerman/Autoloader.php';
use Workerman\Worker;
$ws_worker = new Worker('websocket://0.0.0.0:8000');
显式设置进程数为 CPU 核心数的 2 倍(例如 8 核服务器设为 16):
$ws_worker->count = 16;
【必须在 onWorkerStart 中初始化连接资源,不能在全局或 onConnect 中创建】 因为 onConnect 每次触发都新建对象,而 onWorkerStart 每个进程只执行一次,是唯一安全的初始化时机。
实现长连接状态管理与心跳保活
方法一:使用内置心跳机制(推荐)
设置连接超时时间为 60 秒:
$ws_worker->ping_interval = 25;
$ws_worker->ping_not_response_limit = 2;
这表示每 25 秒发一次 ping,连续 2 次未收到 pong 就主动断开连接。它比手动实现更轻量,且自动兼容所有客户端。
方法二:手动实现自定义心跳
在 onWorkerStart 中启动定时器:
Worker::addTimer(30000, function() use ($ws_worker) {
foreach($ws_worker->connections as $connection) {
if ($connection->isWebSocketConnection && $connection->status === \Workerman\Connection\TcpConnection::STATUS_ESTABLISHED) {
$connection->send("\x00"); // 发送空帧或自定义心跳包
}
}
});
注意:手动心跳需配合 onMessage 中识别心跳帧并重置超时时间,否则无法真正防断连。
安全处理客户端消息与连接生命周期
第一步:在 onConnect 中记录连接元信息
$ws_worker->onConnect = function($connection) {
$connection->client_ip = $connection->getRemoteIp();
$connection->login_time = time();
$connection->uid = null;
};
第二步:onMessage 中严格校验数据格式
$ws_worker->onMessage = function($connection, $data) {
if (!is_string($data) || strlen($data) > 10240) {
$connection->close();
return;
}
$msg = json_decode($data, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$connection->send('{"error":"invalid json"}');
return;
}
};
第三步:onClose 中清理关联资源
$ws_worker->onClose = function($connection) {
if (!empty($connection->uid)) {
// 从全局在线列表中移除该 uid
unset($GLOBALS['online'][$connection->uid]);
}
};
接入异步数据库防止阻塞主线程
方法1:使用 workerman/mysql 组件(适配 MySQL 5.7+)
在 onWorkerStart 中初始化:
$ws_worker->onWorkerStart = function($worker) {
$config = ['host' => '127.0.0.1', 'port' => 3306, 'user' => 'root', 'password' => '123456', 'database' => 'chat'];
$worker->db = new \Workerman\Mysql\Mysql($config);
};
方法2:改用 Swoole MySQL 协程客户端(需 PHP ≥ 8.0 + Swoole ≥ 5.0)
这一步仅当项目已集成 Swoole 且确认无扩展冲突时才启用。Workerman 4.0.34 原生不绑定 Swoole,强行混用会导致事件循环冲突,【不可在同一个进程内同时运行 Workerman 和 Swoole 的 event loop】。
方法3:将耗时 DB 查询转交异步队列
使用 Redis List 或 Beanstalkd 接收查询任务,由独立 worker 进程消费并回写结果通道。这种方式彻底隔离 I/O 延迟,适合读写密集型业务。











