/ Swoole  PHP  协程  高并发  WebSocket  连接池  Laravel Octane  异步编程 

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 的连接池支持,也可以使用 SimpsHyperf 等框架,它们已经帮你封装好了连接池管理逻辑。

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 完全可以胜任高性能微服务、实时推送、游戏后端等过去"不敢想"的场景。

发布评论

热门评论区: