PHP Swoole 协程开发实战:从零打造高并发 Web 服务

Swoole 是什么?为什么 PHP 开发者需要它
长期以来,PHP 被认为是一门"请求-响应"型语言——每个 HTTP 请求独立启动一个 PHP 进程,请求结束后进程销毁,无法维持持久连接,更谈不上并发。这一架构在传统 Web 开发中运转良好,但面对实时通信、高并发 API、微服务等现代需求时,显得力不从心。
Swoole 彻底改变了这一局面。它是一个基于 C/C++ 编写的 PHP 协程框架,为 PHP 带来了:
- 协程(Coroutine):用同步写法实现异步效果,无需回调地狱
- 持久化进程:Worker 进程常驻内存,资源复用,性能倍增
- 内置 HTTP/WebSocket 服务器:无需 Nginx/Apache,PHP 直接监听端口
- 异步 I/O:数据库、Redis、HTTP 请求全部非阻塞
根据官方基准测试,Swoole 模式下的 PHP 应用吞吐量可以达到传统 PHP-FPM 的 5-10 倍,在 WebSocket 场景下优势更为明显。
安装与环境配置
Swoole 作为 PHP 扩展安装,支持 PHP 7.2+(推荐 PHP 8.1+):
# 通过 PECL 安装
pecl install swoole
# 或手动编译安装(推荐,可开启更多特性)
git clone https://github.com/swoole/swoole-src.git
cd swoole-src
phpize
./configure --enable-swoole-curl --enable-openssl --enable-http2
make -j$(nproc)
sudo make install
# 在 php.ini 中添加
echo "extension=swoole.so" >> $(php --ini | grep "Loaded Configuration" | awk '{print $NF}')
# 验证安装
php --ri swoole
Docker 用户可以直接使用官方镜像:
FROM phpswoole/swoole:latest-php8.3
WORKDIR /var/www
COPY . .
RUN composer install --no-dev
EXPOSE 9501
CMD ["php", "server.php"]
第一个协程 HTTP 服务器
用 Swoole 创建一个 HTTP 服务器只需几行代码:
<?php
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
$server = new Server('0.0.0.0', 9501);
$server->set([
'worker_num' => swoole_cpu_num() * 2, // Worker 进程数
'max_request' => 10000, // 防内存泄漏
'enable_coroutine' => true, // 开启协程
]);
$server->on('request', function (Request $request, Response $response) {
// 每个请求在独立协程中执行
$path = $request->server['request_uri'];
switch ($path) {
case '/health':
$response->end(json_encode(['status' => 'ok', 'time' => time()]));
break;
case '/slow':
// 协程睡眠,不阻塞其他请求!
\Swoole\Coroutine::sleep(1);
$response->end('Done after 1 second');
break;
default:
$response->status(404);
$response->end('Not Found');
}
});
echo "Server started at http://0.0.0.0:9501\n";
$server->start();
关键点:即使有 1000 个并发请求触发 /slow 接口,Swoole 也不会开 1000 个线程——协程切换的开销极低,Worker 进程在等待时会自动切换到处理其他请求。
协程与并发请求实战
Swoole 协程最强大的地方在于并发 I/O。以下示例展示如何并行请求多个外部 API,总耗时只取最慢那个:
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Http\Client;
// 必须在协程容器内运行
Coroutine\run(function () {
// 并发请求三个 API
$results = [];
$wg = new Coroutine\WaitGroup();
$apis = [
'user' => ['api.example.com', '/users/1'],
'orders' => ['api.example.com', '/orders?user_id=1'],
'prefs' => ['api.example.com', '/preferences/1'],
];
foreach ($apis as $key => [$host, $path]) {
$wg->add();
Coroutine::create(function () use ($key, $host, $path, &$results, $wg) {
$client = new Client($host, 443, true);
$client->set(['timeout' => 5]);
$client->get($path);
$results[$key] = json_decode($client->body, true);
$wg->done();
});
}
$wg->wait(); // 等待所有协程完成
// 三个请求并行,总耗时 ≈ max(t_user, t_orders, t_prefs)
echo json_encode($results);
});
传统串行写法需要 300ms(3个API各100ms),协程并发只需约 100ms。这正是 Swoole 在聚合查询场景下大放异彩的原因。
协程连接池:数据库高并发的关键
在高并发场景下,每个协程都创建数据库连接会耗尽资源。连接池(Connection Pool)是标准解决方案:
<?php
use Swoole\Database\PDOPool;
use Swoole\Database\PDOConfig;
// 在 server->start() 前初始化连接池(onWorkerStart 中)
$pool = new PDOPool(
(new PDOConfig)
->withHost('127.0.0.1')
->withPort(3306)
->withDbName('myapp')
->withUsername('root')
->withPassword('secret')
->withCharset('utf8mb4'),
64 // 最大连接数
);
// 在 onRequest 回调中使用
$server->on('request', function ($request, $response) use ($pool) {
// 从池中借一个连接(协程安全)
$pdo = $pool->get();
try {
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$request->get['id']]);
$user = $stmt->fetch(\PDO::FETCH_ASSOC);
$response->end(json_encode($user));
} finally {
// 归还连接,切记不能漏掉
$pool->put($pdo);
}
});
Swoole 还内置了 Redis、PostgreSQL 的连接池支持,也可以使用 Simps、Hyperf 等框架,它们已经帮你封装好了连接池管理逻辑。
WebSocket 服务器:实时通信实战
Swoole 的 WebSocket 支持是其最受欢迎的特性之一,用于实现聊天室、实时推送、在线协作等场景:
<?php
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
$server = new Server('0.0.0.0', 9502);
// 存储所有在线连接(生产环境用 Redis)
$connections = [];
$server->on('open', function (Server $server, Request $request) use (&$connections) {
$fd = $request->fd;
$connections[$fd] = [
'fd' => $fd,
'user_id' => $request->get['user_id'] ?? 'anonymous',
'joined' => time(),
];
echo "Client #{$fd} connected\n";
// 广播上线消息
$msg = json_encode(['type' => 'join', 'user' => $connections[$fd]['user_id']]);
foreach ($connections as $conn) {
if ($conn['fd'] !== $fd) {
$server->push($conn['fd'], $msg);
}
}
});
$server->on('message', function (Server $server, Frame $frame) use (&$connections) {
$data = json_decode($frame->data, true);
// 广播消息给所有在线用户
$broadcast = json_encode([
'type' => 'message',
'user' => $connections[$frame->fd]['user_id'],
'content' => htmlspecialchars($data['content'] ?? ''),
'time' => date('H:i:s'),
]);
foreach ($connections as $conn) {
$server->push($conn['fd'], $broadcast);
}
});
$server->on('close', function (Server $server, int $fd) use (&$connections) {
unset($connections[$fd]);
echo "Client #{$fd} disconnected\n";
});
echo "WebSocket server started at ws://0.0.0.0:9502\n";
$server->start();
与 Laravel/Symfony 集成:Octane 方案
如果你已有 Laravel 项目,不需要从头用 Swoole 重写——Laravel Octane 提供了无缝集成:
# 安装 Laravel Octane
composer require laravel/octane
# 安装 Swoole
php artisan octane:install --server=swoole
# 启动(默认 8000 端口,4个 Worker)
php artisan octane:start --workers=4 --max-requests=500
Octane 会在 Worker 进程启动时加载一次框架(服务容器、路由、配置),后续请求直接复用,省去了每次请求重复 bootstrap 的开销。实测 Laravel Octane + Swoole 的 RPS(每秒请求数)比传统 PHP-FPM 高出 3-8 倍。
需要注意的是,Octane 模式下服务容器是跨请求共享的,要特别注意以下几点:
- 避免在服务中存储请求相关的状态(用
scoped绑定代替singleton) - 静态变量会在请求间泄漏,用完后手动清理
- 数据库连接由 Octane 管理,不要手动关闭
性能调优与生产部署建议
将 Swoole 应用推上生产环境前,还有几个关键配置需要关注:
- Worker 进程数:CPU 密集型建议等于 CPU 核数,I/O 密集型可设为 CPU 核数的 2-4 倍
- max_request:设置每个 Worker 最大处理请求数后自动重启,有效防止内存泄漏
- daemonize:生产环境设为 true,配合 Supervisor 管理进程
- log_file:指定日志文件路径,并配合 logrotate 定期轮转
# /etc/supervisor/conf.d/swoole.conf
[program:swoole-app]
command=php /var/www/server.php
directory=/var/www
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/swoole/app.log
Swoole 的引入意味着你的 PHP 应用从"无状态 CGI"进化为"常驻进程服务",思维模式的转变比代码迁移更重要。一旦掌握协程编程范式,你会发现 PHP 完全可以胜任高性能微服务、实时推送、游戏后端等过去"不敢想"的场景。
发布评论
热门评论区: