worker实例本身不支持单实例监听多个端口,必须通过多个实例或动态创建实现;直接new多个worker对象是最稳妥、兼容性最好的方式,php版本无关,也无需处理复用逻辑。

Worker 实例本身不支持单实例监听多个端口,必须通过多个实例或动态创建实现。直接 new 多个 Worker 对象是最稳妥、兼容性最好的方式,PHP 版本无关,也无需处理复用逻辑。
多个 Worker 实例分别监听不同端口
这是最常用、最不易出错的多端口方案,适合 HTTP + WebSocket + TCP 混合部署场景。
-
Worker构造时传入完整协议+地址+端口,如'http://0.0.0.0:8080'、'websocket://0.0.0.0:2346'、'tcp://127.0.0.1:5566' - 每个实例独立设置
onMessage、onConnect等回调,互不影响 - 进程数(
count)按需分配:HTTP 服务可设 4,TCP 短连接可设 8,避免所有实例共用同一count - 启动顺序无关,
Worker::runAll()会统一调度所有已注册实例
示例:
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
$http = new Worker('http://0.0.0.0:8080');
$http->count = 4;
$http->onMessage = function ($connection, $data) {
$connection->send("HTTP OK");
};
$ws = new Worker('websocket://0.0.0.0:2346');
$ws->count = 2;
$ws->onMessage = function ($connection, $data) {
$connection->send("WS echo: $data");
};
$tcp = new Worker('tcp://127.0.0.1:5566');
$tcp->count = 8;
$tcp->onMessage = function ($connection, $data) {
$connection->send("TCP received: " . strlen($data) . " bytes");
};
Worker::runAll();
同一个进程内动态监听额外端口(listen())
适用于需要共享内存变量、跨协议通信的场景,比如 HTTP 请求触发向 WebSocket 客户端广播。
- 必须在
onWorkerStart回调中调用$inner_worker->listen(),不能在全局作用域调用 - 动态创建的
Worker不会继承父进程的count,它只在当前子进程中生效 - 若多个子进程都尝试
listen()同一端口(如 2016),PHP Address already in use - PHP ≥ 7.0 时,必须显式设置
$inner_worker->reusePort = true,否则仍会失败
错误写法(PHP 7.0+ 也不安全):
$worker->onWorkerStart = function($worker) {
$inner = new Worker('http://0.0.0.0:2016');
// ❌ 缺少 reusePort = true → 多进程下必然报错
$inner->listen(); // Address already in use
};
reusePort = true 的真实作用和限制
它不是“让多个进程监听同一端口变得可行”的万能开关,而是启用内核级 SO_REUSEPORT 的开关 —— 仅对完全相同的监听地址有效,且依赖系统支持。
- 仅当多个
Worker实例监听**完全相同**的host:port时才需要它(例如 4 个进程都监听http://0.0.0.0:2016) - 它不能解决
Permission denied(端口 - Linux 内核需 ≥ 3.9;macOS 不支持
SO_REUSEPORT(会静默忽略该设置) - 开启后,连接分发由内核完成,但各进程间仍**不共享连接对象或内存状态**,只是负载更均衡
端口冲突排查优先级
监听失败时,别急着改 reusePort,先确认根本原因:
- 执行
netstat -tuln | grep :端口号或lsof -i :端口号,确认是否真被占用 - 检查绑定 IP:用
127.0.0.1能通但0.0.0.0不行?可能是防火墙或云服务器安全组未放行 - 非 root 用户尝试绑定
:80或:443→ 改用高位端口(如:8080)或加sudo - SELinux 启用时,
setsebool -P httpd_can_network_bind 1才允许 PHP 绑定端口
真正容易被忽略的是:动态 listen() 创建的 Worker 不会自动继承父 Worker 的协议处理器(如 HttpServer 类),如果手动 new Worker('http://...') 却没加载对应协议类,onMessage 可能根本不会触发 —— 这类问题不会报错,只会“静默失效”。











