本文目录导读:

- 什么是 Worker 模式?为什么 PHP 需要它?
- Worker 模式的核心机制:进程模型与生命周期
- 手写一个极简 PHP Worker(核心代码)
- 主流实现:Workerman / Swoole 的 Worker 异同
- 高频问题问答(Q&A)
- 性能调优与常见坑(重点)
- 总结:何时该用 Worker 模式?
**
《PHP 长驻内存与高并发利器:深度解析 Worker 模式原理、实战与避坑指南》
目录导读
- 什么是 Worker 模式?为什么 PHP 需要它?
- Worker 模式的核心机制:进程模型与生命周期
- 手写一个极简 PHP Worker(附代码)
- 主流实现:Workerman / Swoole 的 Worker 异同
- 高频问题问答(Q&A)
- 性能调优与常见坑(内存泄漏、连接复用)
- 何时该用 Worker 模式?
什么是 Worker 模式?为什么 PHP 需要它?
传统的 PHP-CGI/FPM 模式,每个请求经历“加载→执行→销毁”全生命周期,所有类、连接、变量在请求结束后清空,这种“无状态”设计虽简单,但在长连接(WebSocket)、TCP 服务、异步任务队列、秒级定时器场景下,性能会直线下降——每次请求重复创建数据库连接、重复编译模板、重复加载框架。
Worker 模式(常称“常驻进程模型”)指:在 CLI 环境下启动一批(一个或多个)PHP 进程,它们常驻内存,通过事件循环(Event Loop)分发任务,执行完不退出,继续等待下一个任务,这就像永远开启的“服务员”,省去了频繁“招聘(进程创建)和离职(进程销毁)”的开销。
Worker 模式的核心机制:进程模型与生命周期
- Master 进程:负责创建 Worker 子进程、监控信号、平滑重载。
- Worker 进程:每个 Worker 内部运行一个事件循环(基于
stream_select、epoll或event扩展),监听多个连接(非阻塞 I/O)。 - 生命周期:
onWorkerStart(启动时初始化连接池/加载配置)→onReceive/onMessage(业务逻辑)→onClose(释放资源),整个周期内,类定义、静态属性、数据库连接都复用,不再每次重建。
关键点:需要配合 pcntl_fork(进程创建)与 posix_kill(信号控制),且必须运行在 CLI 模式下(php your_worker.php start)。
手写一个极简 PHP Worker(核心代码)
<?php
// worker.php
$workerCount = 4;
$pidFile = '/tmp/my_worker.pid';
// 1. Master 进程 Fork 多个 Worker
for ($i = 0; $i < $workerCount; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
die("Fork failed");
} elseif ($pid) {
// Master: 记录子进程 PID
$pids[] = $pid;
} else {
// Worker: 进入事件循环
$server = stream_socket_server("tcp://0.0.0.0:8080", $errno, $errstr);
while (true) {
$conn = @stream_socket_accept($server, -1);
if ($conn) {
fwrite($conn, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK");
fclose($conn);
}
// 模拟处理完不销毁进程,继续循环
}
exit(0); // 不会执行到
}
}
// Master 等待信号
pcntl_signal(SIGTERM, function () use ($pids) {
foreach ($pids as $pid) { posix_kill($pid, SIGTERM); }
});
while (true) { pcntl_signal_dispatch(); sleep(1); }
这个例子展示了核心:Fork + 非阻塞 accept + 无限循环,真实项目中,你需要用 stream_select 或 swoole_event 避免阻塞,实际生产强烈建议直接用现成框架(Workerman/Swoole),因为手写容易踩坑(僵尸进程、信号竞争)。
主流实现:Workerman / Swoole 的 Worker 异同
| 对比项 | Workerman (纯 PHP) | Swoole (C 扩展) |
|---|---|---|
| 底层 | stream_select + pcntl |
epoll + 多线程/多进程 |
| 性能 | 中等(单机 1-3 万并发) | 高(单机 10 万+ 长连接) |
| 学习曲线 | 低(纯 PHP 语法) | 较高(需理解协程/异步回调) |
| 适用范围 | WebSocket、TCP 服务、微服务 | 高性能 API、RPC、游戏服务器 |
Worker 模式在两者中均表现为 worker_num 参数:
- Workerman:
$worker->count = 4; - Swoole:
new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $server->set(['worker_num'=>4]);
最佳实践:
- 对 PHP 熟练度一般,性能要求不苛刻 → Workerman。
- 需要高并发(>50k 连接)、原生协程、更细粒度内存控制 → Swoole。
高频问题问答(Q&A)
Q1:Worker 模式会内存泄漏吗?
A:会,常驻进程不释放全局变量,所以必须避免在循环内无限 new 对象未清理;使用 gc_collect_cycles() 或框架自带连接池(如 Workerman 的 ConnectionPool)定时回收。
Q2:Worker 模式能用 PHP-FPM 吗?
A:不能,FPM 属于“请求驱动型”,本质是短生命周期进程,无法维持长连接状态,必须用 CLI 模式启动独立进程。
Q3:多个 Worker 进程间如何共享数据?
A:用 Redis 或 共享内存(shmop),不建议用文件锁(性能差),Workerman 提供 Channel 组件,Swoole 用 Table。
Q4:代码改了之后,如何平滑重启 Worker?
A:Master 进程收到 SIGUSR1 信号后,先 fork 新 Worker,再杀掉旧 Worker(预热式重启),Workerman 自带 reload 命令,Swoole 为 $server->reload()。
性能调优与常见坑(重点)
- 坑1:阻塞函数灾难 — 在事件循环里绝不能使用
sleep()、file_get_contents(同步)、mysqli同步查询,必须换成异步/协程版本(如Swoole\Coroutine\Http\Client)。 - 坑2:连接复用陷阱 — Worker 启动时建立的 MySQL 连接,若 MySQL
wait_timeout较短,空闲会断连,需要加ping重连机制或使用Swoole连接池。 - 坑3:进程安全 —
pcntl_fork后,Worker 进程中不能再 fork 新进程,否则会变成“子子孙孙”,业务逻辑中禁止shell_exec长时间任务。 - 调优建议:
worker_num= CPU 核心数(I/O 密集可 2-4 倍)。- 开启
daemonize(守护进程) + 日志监控。 - 使用
opcache.preload预加载常用框架类(Swoole/Workerman 支持)。
何时该用 Worker 模式?
强烈推荐使用场景:
- WebSocket 聊天/推送(Worker 保持长连接)
- 异步短信/邮件队列(Worker 消费 Redis 队列)
- 自建 TCP 网关(如智能硬件数据收包)
- 定时任务(秒级 cron 替代方案)
无需使用的场景:
- 传统 PHP+MySQL 博客(FPM 足够)
- 一次性的 CLI 脚本(直接跑完退出)
Worker 模式让 PHP 摆脱“请求即可焚”的枷锁,但代价是引入进程管理和内存控制复杂度,建议从 Workerman 入门,理解事件循环思想,再评估是否迁移 Swoole,掌握它,你的 PHP 将具备真正的“常青藤”级并发能力。