本文目录导读:

在PHP项目中使用Swoole协程,主要分为以下几个步骤,我会从基础概念、环境搭建、实际代码示例到生产注意事项,给你一个完整的指南。
理解Swoole协程的核心概念
Swoole协程允许你用同步的代码写法实现异步IO的性能,关键点:
- 协程是用户态轻量级线程,创建开销极低(约2KB内存)
go()函数创建协程- 遇到IO操作(如MySQL查询、Redis读写、HTTP请求)时自动挂起,释放CPU
- IO完成后自动恢复执行
环境搭建
# 安装Swoole扩展(推荐4.8+或5.x版本) pecl install swoole # 或编译安装(更稳定) phpize && ./configure && make && make install # 验证安装 php -m | grep swoole # 必须开启协程钩子(在代码或php.ini中设置) ; php.ini 或代码中 swoole.use_shortname = 'Off' ; 可选,建议关闭短函数名
重要: Swoole 5.x及以上版本默认开启协程钩子,不需要额外设置。
基础协程示例
1 创建和运行协程
<?php
// 启用协程钩子(Swoole 4.4+)
\Swoole\Runtime::enableCoroutine();
// 创建协程
go(function () {
echo "Hello from coroutine 1\n";
// 模拟IO操作
\Swoole\Coroutine::sleep(1);
echo "After sleep in coroutine 1\n";
});
go(function () {
echo "Hello from coroutine 2\n";
\Swoole\Coroutine::sleep(0.5);
echo "After sleep in coroutine 2\n";
});
echo "Main process continues...\n";
输出:
Hello from coroutine 1
Hello from coroutine 2
Main process continues...
After sleep in coroutine 2
After sleep in coroutine 1
2 并发执行多个任务
<?php
use Swoole\Coroutine;
use function Swoole\Coroutine\run;
run(function () {
// 并发执行3个任务
$tasks = [
'task1' => function () {
Coroutine::sleep(2);
return "Task 1 result";
},
'task2' => function () {
Coroutine::sleep(1);
return "Task 2 result";
},
'task3' => function () {
Coroutine::sleep(3);
return "Task 3 result";
}
];
// 使用WaitGroup等待所有协程完成
$wg = new Coroutine\WaitGroup();
$results = [];
foreach ($tasks as $key => $task) {
$wg->add();
go(function () use ($key, $task, $wg, &$results) {
$results[$key] = $task();
$wg->done();
});
}
$wg->wait();
print_r($results);
// 总耗时约3秒(最长的那个任务),而不是6秒
});
实际项目中的典型场景
1 并发HTTP请求(替代curl_multi)
<?php
use Swoole\Coroutine\Http\Client;
use function Swoole\Coroutine\run;
run(function () {
$urls = [
'http://api.example.com/data1',
'http://api.example.com/data2',
'http://api.example.com/data3'
];
$results = [];
$wg = new \Swoole\Coroutine\WaitGroup();
foreach ($urls as $index => $url) {
$wg->add();
go(function () use ($url, $index, $wg, &$results) {
$client = new Client(parse_url($url, PHP_URL_HOST), 80);
$client->get(parse_url($url, PHP_URL_PATH));
$results[$index] = $client->body;
$client->close();
$wg->done();
});
}
$wg->wait();
print_r($results);
// 总耗时 = 最慢的请求时间(非累加)
});
2 并发数据库查询(MySQL)
<?php
use Swoole\Coroutine\MySQL;
use function Swoole\Coroutine\run;
run(function () {
// 创建连接池(生产环境请使用连接池库如easyswoole/pool)
$pool = new \Swoole\Coroutine\Channel(10);
// 初始化连接池
for ($i = 0; $i < 5; $i++) {
$db = new MySQL();
$db->connect([
'host' => '127.0.0.1',
'port' => 3306,
'user' => 'root',
'password' => 'password',
'database' => 'test',
'charset' => 'utf8mb4',
]);
$pool->push($db);
}
// 并发执行多个查询
$queries = ['SELECT 1', 'SELECT 2', 'SELECT 3'];
$wg = new \Swoole\Coroutine\WaitGroup();
foreach ($queries as $sql) {
$wg->add();
go(function () use ($pool, $sql, $wg) {
$db = $pool->pop(); // 从池中获取连接
$result = $db->query($sql);
var_dump($result);
$pool->push($db); // 归还连接
$wg->done();
});
}
$wg->wait();
});
在现有框架中使用Swoole协程
1 结合Laravel/Lumen
// 安装swoole_laravel扩展或自己实现
composer require swooletw/laravel-swoole
// 配置文件 config/swoole_http.php
return [
'server' => [
'host' => '0.0.0.0',
'port' => 1215,
],
];
// 启动命令
php artisan swoole:http start
// 在控制器中使用协程
public function index()
{
$result = \Swoole\Coroutine\run(function () {
// 协程安全的数据库查询
$user = User::find(1); // 自动使用协程MySQL
$posts = Post::where('user_id', 1)->get();
// 并发HTTP请求
$client = new \Swoole\Coroutine\Http\Client('api.example.com', 80);
$client->get('/data');
return [
'user' => $user,
'posts' => $posts,
'api_data' => $client->body
];
});
return response()->json($result);
}
2 结合ThinkPHP
// 安装topthink/think-swoole
composer require topthink/think-swoole
// 启动
php think swoole start
// 控制器中使用
class Index
{
public function index()
{
$result = \Swoole\Coroutine\run(function () {
// 自动协程化的ORM操作
$users = User::select();
return $users;
});
return json($result);
}
}
协程安全注意事项
1 避免全局状态竞争
// ❌ 危险:全局变量在不同协程间共享
$counter = 0;
go(function () use (&$counter) {
$counter++; // 可能导致数据竞争
});
// ✅ 安全:使用Channel传递数据
$chan = new \Swoole\Coroutine\Channel();
go(function () use ($chan) {
$chan->push(1);
});
$result = $chan->pop();
2 使用协程安全的组件
// ❌ 不安全的全局单例
$db = new PDO(...); // PDO不是协程安全的
// ✅ 安全的做法:每个协程创建独立连接,或使用连接池
go(function () {
$db = new \Swoole\Coroutine\MySQL();
$db->connect([...]);
});
3 协程上下文管理
// 使用Context管理请求级别的数据
use Swoole\Coroutine\Context;
// 在协程开始处设置
$context = new Context();
$context['user_id'] = 123;
go(function () use ($context) {
// 在协程内使用
$userId = $context['user_id'];
});
生产环境部署建议
1 最佳实践配置
// 创建HTTP服务器
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->set([
'worker_num' => swoole_cpu_num() * 2, // 根据CPU核心数设置
'max_request' => 5000, // 防止内存泄漏
'enable_coroutine' => true, // 开启协程
'hook_flags' => SWOOLE_HOOK_ALL, // 全部协程化
]);
$http->on('request', function ($request, $response) {
// 每个请求自动在协程中运行
$response->header('Content-Type', 'text/plain');
$response->end("Hello World\n");
});
$http->start();
2 部署脚本
# 启动 php server.php start # 守护进程模式 php server.php start -d # 热重启 kill -USR1 [master_pid] # 查看状态 ps aux | grep swoole
3 监控指标
// 在代码中获取协程状态 echo "当前协程数: " . \Swoole\Coroutine::stats()['coroutine_num'] . "\n"; echo "Coro总创建数: " . \Swoole\Coroutine::stats()['coroutine_peak_num'] . "\n";
常见问题排查
| 问题 | 解决方案 |
|---|---|
| 协程栈溢出 | 增加 stack_size 配置(默认2M) |
| 连接超时 | 设置 timeout 参数,添加断线重连逻辑 |
| 内存泄漏 | 检查是否所有资源都被释放,使用 max_request 限制 |
| 协程死锁 | 避免在协程间使用互斥锁,优先使用Channel |
使用Swoole协程的关键公式:
- 启用协程钩子 → 自动将同步IO转为协程IO
- 使用
go()创建任务 → 实现并发 - 使用
WaitGroup或Channel→ 同步协程结果 - 使用连接池 → 管理资源和限制并发数
- 避免全局状态共享 → 使用Channel传递数据
Swoole协程能让你在保持代码可读性的同时,获得接近Node.js的并发性能,建议从小规模功能开始尝试,逐步替换现有项目中的IO密集操作。