PHP 异步怎么实现?Swoole、Workerman 使用场景对比

PHP 异步怎么实现?Swoole、Workerman 使用场景对比

引言

传统 PHP-FPM 是"请求进来→执行脚本→退出进程"的同步模型,天然不适合长连接、实时推送、高并发 IO 编排。

但现代 PHP 已经能很好地做异步:消息队列、常驻进程、事件驱动、协程,四条路各有适用边界。

本文系统讲清楚 PHP 异步的几种实现方式,并重点对比 Swoole 与 Workerman 的底层差异、选型逻辑和落地代码。

目录

  1. PHP 为什么"看起来不支持异步"
  2. PHP 异步的 4 类主流方案
  3. 消息队列 + Worker(最稳的生产方案)
  4. Swoole:协程异步(高性能 IO 密集)
  5. Workerman:纯 PHP 事件驱动(长连接利器)
  6. Swoole vs Workerman 核心对比
  7. 场景化选型决策树
  8. 常驻内存服务的 6 个避坑点

一、PHP 为什么"看起来不支持异步"

  • PHP 语言本身没有 Node.js 那种原生事件循环
  • FPM 模式下:一个请求一个进程,执行完就销毁
  • 阻塞调用(curl_exec、sleep、同步 PDO)会卡住整个进程
  • "异步"不是语法特性,而是运行模型 + IO 调度问题

所以 PHP 做异步,本质是换运行模型:

FPM(同步) → CLI 常驻(事件/协程) → 队列解耦(异步任务)


二、PHP 异步的 4 类主流方案

方案 异步能力 可靠性 适用场景
消息队列 + Worker 任务异步 高 业务系统首选
Swoole 协程 IO 并发 中高 高并发接口、网关
Workerman 事件驱动 长连接 中高 WebSocket、IM、物联网
pcntl/Cron/脚本 简单后台任务 低中 导出、统计、补数据

三、消息队列 + Worker(最稳的生产方案)

1. 流程

复制代码
FPM 请求
  └─> 投递任务到 Redis/RabbitMQ
        └─> 独立 CLI Worker 消费执行
              └─> 发邮件 / 推消息 / 写日志 / 调第三方

2. 投递任务(FPM 侧)

复制代码
// 用 Redis 简单队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$task = [
    'type' => 'send_email',
    'to'   => 'user@example.com',
    'data' => ['order_id' => 1001],
];

$redis->rPush('queue:email', json_encode($task));

3. Worker 消费(CLI 常驻)

复制代码
while (true) {
    $raw = $redis->blPop('queue:email', 5);
    if (!$raw) continue;

    $task = json_decode($raw[1], true);
    try {
        sendEmail($task['to'], $task['data']);
        markDone($task);
    } catch (Throwable $e) {
        // 重试 / 死信队列
        $redis->rPush('queue:email:failed', $raw[1]);
    }
}

用 Supervisor 保活:

复制代码
[program:email-worker]
command=php /app/worker/email.php
autostart=true
autorestart=true
numprocs=4

适合:90% 的业务系统(Laravel/ThinkPHP 直接上队列,别先想 Swoole)


四、Swoole:协程异步(高性能 IO 密集)

核心模型

  • C 扩展,底层 epoll/kqueue
  • Reactor 线程 + Worker 进程 + 协程调度
  • 协程内遇到 IO 自动挂起,不阻塞进程

HTTP 并发调用示例

复制代码
Swoole\Coroutine\run(function () {
    $client = new Swoole\Coroutine\Http\Client('api.example.com', 80);
    $client->set(['timeout' => 2]);

    $client->get('/user/1');
    $user = $client->getBody();

    $client->get('/order/1001');
    $order = $client->getBody();

    // 同步写法,并发执行
    var_dump($user, $order);
});

WebSocket 服务

复制代码
$server = new Swoole\WebSocket\Server('0.0.0.0', 9501);

$server->on('open', function ($server, $req) {
    echo "client {$req->fd} connected\n";
});

$server->on('message', function ($server, $frame) {
    // 协程内查库、推消息
    $server->push($frame->fd, 'recv: ' . $frame->data);
});

$server->on('close', function ($server, $fd) {
    echo "client {$fd} closed\n";
});

$server->start();

适合

  • 高 QPS API 网关
  • 微服务(Hyperf)
  • 实时风控、订单路由
  • 大量并发 HTTP/Redis/MySQL 调用

五、Workerman:纯 PHP 事件驱动(长连接利器)

核心模型

  • 纯 PHP(pcntl + stream_select / epoll)
  • 多进程 + 事件回调
  • 无原生协程(Webman 2.x 可用 Fiber 模拟)

TCP/WebSocket 示例

复制代码
use Workerman\Worker;

$ws = new Worker('websocket://0.0.0.0:2345');

$ws->onConnect = function ($conn) {
    echo "new connection\n";
};

$ws->onMessage = function ($conn, $data) {
    // 注意:这里不能写 file_get_contents 阻塞调用
    $conn->send('hello: ' . $data);
};

$ws->onClose = function ($conn) {
    echo "close\n";
};

Worker::runAll();

启动:

复制代码
php start.php start -d

适合

  • 聊天室、客服系统、推送
  • 物联网设备心跳(MQTT/Modbus)
  • 不想装 C 扩展、运维受限环境
  • 中小并发长连接(几万级很稳)

六、Swoole vs Workerman 核心对比

维度 Swoole Workerman
底层 C 扩展 纯 PHP
并发模型 协程 + Reactor 多进程事件驱动
异步写法 同步风格协程 回调/事件
性能 更高(IO 密集) 高(够用)
长连接 强 强且易调试
部署 需编译扩展 Composer 引入
调试 难(协程上下文) 容易(Xdebug 友好)
学习曲线 陡 平缓
上层框架 Hyperf / EasySwoole Webman / GatewayWorker
内存占用 更低 略高

七、场景化选型决策树

1. 普通业务系统(后台/CMS/电商后台)

复制代码
FPM + 框架 + Redis 队列
  • 别上 Swoole
  • 导出、通知、统计全丢队列
  • 稳定、好调试、改代码即时生效

2. 需要 WebSocket / IM / 推送

  • 能装扩展、要性能 → Swoole
  • 不能装扩展、快速交付 → Workerman / Webman

3. 高并发 API / 微服务

  • IO 密集、多服务聚合 → Swoole + Hyperf
  • 中等并发、要简单 → Webman

4. 物联网 / 私有协议

  • 设备接入、TCP 解析 → Workerman(协议灵活)
  • 超大规模接入 → Swoole

5. 混合架构(最推荐)

复制代码
Nginx
├─ /admin        → FPM(Laravel/ThinkPHP)
├─ /api          → Webman / Hyperf
├─ /ws           → Swoole/Workerman WebSocket
└─ 队列消费       → CLI Worker(Supervisor)

八、常驻内存服务的 6 个避坑点

1. 全局/静态变量跨请求污染

复制代码
// 错误
class UserService {
    public static $uid;
}

// 正确:用请求上下文或协程上下文
$context = Swoole\Coroutine::getContext();
$context['uid'] = 1001;

2. 连接不能写全局

复制代码
// 错误:启动一次,请求间复用会串
$db = new PDO(...);

// 正确:连接池 / 协程内获取
$pool->getConnection()->query(...)

3. 阻塞函数会卡死

  • sleep() → 用 Co::sleep()
  • file_get_contents → 用协程 HTTP Client
  • 同步 PDO → 用 Swoole MySQL 协程客户端

4. 代码热更新

  • FPM:改了就生效
  • Swoole/Workerman:reload 或 restart
  • Supervisor 必须前台运行,不能 -d 再托管

5. 心跳与僵尸连接

WebSocket 必须:

  • 客户端发 ping
  • 服务端检测 lastMessageTime
  • 超时断开,释放 fd

6. 日志与监控

  • 业务日志走 Monolog
  • 慢请求:Swoole slowlog / Workerman 自定义计时
  • 进程数:CPU 核数 × 2~4
  • 系统参数:ulimit -n、tcp_tw_reuse

九、一句话总结

  • 业务异步:先上「消息队列 + Worker」,别滥用常驻服务
  • 高并发 IO:选 Swoole(协程、性能天花板)
  • 长连接/快速交付/不能装扩展:选 Workerman
  • 真实生产:FPM + 队列 + Webman/Swoole 混合部署最稳

PHP 异步不是"二选一",而是按业务拆层:

同步请求走 FPM,耗时任务走队列,实时通道走 Swoole/Workerman。

相关推荐
十二同学啊1 小时前
Python 工程化详解:项目结构、依赖管理、代码质量、测试与部署
java·开发语言·python
乐橙开放平台1 小时前
跨地域监控难管理?通过 GB28181 把各地海康大华乐橙摄像头注册到云端平台
开发语言·笔记·物联网·自动化·php·音视频·智能家居
广东融科数据服务有限公司1 小时前
网络监控经常卡顿、掉线、画面模糊?一线排查问答实录
开发语言·网络·php
Sarvartha1 小时前
Iterator 迭代器、ListIterator & foreach 循环
开发语言
樱花落木兰1 小时前
Redis 消息队列的可靠性演进:从 List、Pub/Sub 到 Stream 消费者组,以及 8.x 版本改变了什么
java·开发语言·经验分享·redis·分布式·python·软件工程
外收内放2 小时前
ENSP学习记录(6)
开发语言·学习·php
dialect_2 小时前
CODEFORCES ROUND 1127 DIV2
开发语言·c++·算法
盛世宏博北京2 小时前
档案库房恒温恒湿消毒净化一体机与3D可视化平台联动调试笔记
开发语言·php·温湿度·档案室
小徐不徐说10 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php