PHP 怎么Worker模式

wen PHP项目 2

本文目录导读:

PHP 怎么Worker模式

  1. 什么是 Worker 模式?为什么 PHP 需要它?
  2. Worker 模式的核心机制:进程模型与生命周期
  3. 手写一个极简 PHP Worker(核心代码)
  4. 主流实现:Workerman / Swoole 的 Worker 异同
  5. 高频问题问答(Q&A)
  6. 性能调优与常见坑(重点)
  7. 总结:何时该用 Worker 模式?

**
《PHP 长驻内存与高并发利器:深度解析 Worker 模式原理、实战与避坑指南》


目录导读

  1. 什么是 Worker 模式?为什么 PHP 需要它?
  2. Worker 模式的核心机制:进程模型与生命周期
  3. 手写一个极简 PHP Worker(附代码)
  4. 主流实现:Workerman / Swoole 的 Worker 异同
  5. 高频问题问答(Q&A)
  6. 性能调优与常见坑(内存泄漏、连接复用)
  7. 何时该用 Worker 模式?

什么是 Worker 模式?为什么 PHP 需要它?

传统的 PHP-CGI/FPM 模式,每个请求经历“加载→执行→销毁”全生命周期,所有类、连接、变量在请求结束后清空,这种“无状态”设计虽简单,但在长连接(WebSocket)、TCP 服务、异步任务队列、秒级定时器场景下,性能会直线下降——每次请求重复创建数据库连接、重复编译模板、重复加载框架。

Worker 模式(常称“常驻进程模型”)指:在 CLI 环境下启动一批(一个或多个)PHP 进程,它们常驻内存,通过事件循环(Event Loop)分发任务,执行完不退出,继续等待下一个任务,这就像永远开启的“服务员”,省去了频繁“招聘(进程创建)和离职(进程销毁)”的开销。

Worker 模式的核心机制:进程模型与生命周期

  • Master 进程:负责创建 Worker 子进程、监控信号、平滑重载。
  • Worker 进程:每个 Worker 内部运行一个事件循环(基于 stream_selectepollevent 扩展),监听多个连接(非阻塞 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_selectswoole_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 将具备真正的“常青藤”级并发能力。

抱歉,评论功能暂时关闭!