定位
Swoole 不是框架,是 PHP 扩展。 它给 PHP 加上协程、常驻内存、异步 IO 的能力,但不提供路由、ORM、模板。
装好 Swoole 之后,你依然需要框架——Hyperf 之所以能跑协程,是因为 Swoole 提供了底层运行时。
把这点搞清楚,后面所有讨论才成立。
安装
# 方式一:pecl
pecl install swoole
1. 方式二:源码编译(需要定制编译选项时)
wget https://github.com/swoole/swoole-src/archive/refs/tags/v6.0.0.tar.gz
tar -xzf v6.0.0.tar.gz && cd swoole-src-6.0.0
phpize && ./configure --enable-openssl --enable-http2 --enable-swoole-curl
make -j && sudo make install
php.ini 里启用:
extension=swoole.so
验证:
php -m | grep swoole
php --ri swoole # 看编译选项
核心概念
1. 协程:用同步写法做异步
<?php
// 传统阻塞写法,三个请求串行,总耗时 = 1 + 2 + 3 = 6 秒
$a = file_get_contents('https://api.a.com'); // 阻塞
$b = file_get_contents('https://api.b.com');
// 协程写法,用 Swoole 的协程客户端
use SwooleCoroutine;
use function SwooleCoroutinerun;
run(function () {
$results = [];
// 并发发起三个请求,总耗时约等于最慢的那个
Coroutineparallel(3, function ($i) use (&$results) {
$cli = new CoroutineHttpClient('api.example.com', 443, true);
$cli->get('/');
$results[$i] = $cli->body;
});
});
关键:协程不是并行计算,是「IO 时让出 CPU」。所以它只对 IO 密集场景有效(网络请求、数据库、文件)。
2. 必须用协程化的客户端
这是最重要的一条。 普通 PHP 函数是阻塞的,在协程里调用会堵死整个进程:
| 用途 | 阻塞(不能用) | 协程化(要用) |
|---|---|---|
| HTTP | curl、file_get_contents | SwooleCoroutineHttpClient |
| MySQL | PDO、mysqli | SwooleCoroutineMySQL |
| Redis | redis 扩展 | SwooleCoroutineRedis |
| 文件 | file_get_contents | SwooleCoroutineSystem::readFile |
一个 file_get_contents 就能让整个服务的并发能力掉到个位数,而且不报错。
Swoole 提供了一键协程化的方案:
; php.ini 里开启运行时钩子
swoole.enable_library=php_stream
swoole.enable_coroutine=true
开了钩子后,PDO、curl、file_get_contents 会被自动替换成协程版本。这是最容易落地的方案,但要注意兼容性。
3. 常驻内存:全局状态会残留
// 危险:这个静态数组会在所有请求间共享
class Cache
{
public static array $data = [];
}
在 FPM 里每次请求都是新进程,静态变量清空。在 Swoole 常驻进程里,它会一直累积。
解决办法:用 SwooleCoroutine::getContext() 存请求级数据:
use SwooleCoroutine;
Coroutine::getContext()['user_id'] = $id; // 只属于当前协程
4. 两种运行模式
| 模式 | 用法 | 适合 |
|---|---|---|
| 协程容器 | Coroutinerun(fn() => ...) | 脚本、CLI、和现有 FPM 项目混用 |
| 常驻 Server | SwooleHttpServer 等 | 完整的高性能服务 |
常见坑
① 编译选项不全
--enable-openssl、--enable-http2、--enable-swoole-curl 这些编译选项决定了能力。用 pecl 默认编译可能缺功能,后期改要重新编译。
② 协程内不能用 sleep
sleep() 会阻塞整个进程。要用 SwooleCoroutine::sleep()。
③ 超全局变量不可靠
$_GET、$_POST、$_SERVER 在协程间会串。必须用 Swoole 提供的请求对象。
④ 版本差异大
4.x 和 5.x 的协程实现有变化(5.x 后更稳定),6.x 又加入了新的特性。教程版本要先确认。
什么时候用它
直接装 Swoole 的合理场景:
- 用 Hyperf(它依赖 Swoole)
- 需要写常驻进程但不想引入 Workerman
- 现有项目想给某几个接口加速(用协程容器包一层)
不合适:想「装个扩展就让网站变快」。网站变快靠的是减少数据库查询、加缓存,不是换运行时。
一个务实的路径
如果你的目标是「让 PHP 服务更快」,建议的顺序是:
- 先加 OPcache(几行配置,立竿见影)
- 再做对象缓存(Redis 缓存热点查询)
- 然后 profile,找到真正的瓶颈
- 最后才考虑协程(如果瓶颈确实在「每请求初始化」)
跳过前三步直接上 Swoole,通常得到的是「更快的错误」——因为业务逻辑和查询没优化,协程只是让同样的查询跑得更快一点。