Skip to content

PHP Swoole实战指南

引言

Swoole是一个为PHP提供高性能网络通信能力的扩展。本文将介绍如何使用Swoole构建高性能的PHP应用。

基础概念

什么是Swoole

Swoole是一个PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了多进程、异步IO、协程等特性。

环境搭建

安装Swoole

bash
# 通过PECL安装
pecl install swoole

# 或者从源码编译
wget https://github.com/swoole/swoole-src/archive/v4.8.12.tar.gz
tar zxvf v4.8.12.tar.gz
cd swoole-src-4.8.12
phpize
./configure
make && make install

配置PHP

ini
[swoole]
extension=swoole.so

HTTP服务器

基础HTTP服务器

php
<?php

use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;

$server = new Server("0.0.0.0", 9501);

$server->on("start", function (Server $server) {
    echo "Swoole http server is started at http://0.0.0.0:9501\n";
});

$server->on("request", function (Request $request, Response $response) {
    $response->header("Content-Type", "text/plain");
    $response->end("Hello World\n");
});

$server->start();

路由处理

php
<?php

class Router
{
    private $routes = [];

    public function get($path, $handler)
    {
        $this->routes["GET"][$path] = $handler;
    }

    public function post($path, $handler)
    {
        $this->routes["POST"][$path] = $handler;
    }

    public function dispatch($request)
    {
        $method = $request->server["request_method"];
        $path = $request->server["request_uri"];

        if (isset($this->routes[$method][$path])) {
            return $this->routes[$method][$path]($request);
        }

        return ["status" => 404, "message" => "Not Found"];
    }
}

// 使用路由
$router = new Router();

$router->get("/users", function ($request) {
    return ["status" => 200, "data" => ["users" => []]];
});

WebSocket服务器

WebSocket实现

php
<?php

use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;

$server = new Server("0.0.0.0", 9502);

$server->on("start", function (Server $server) {
    echo "WebSocket server is started at ws://0.0.0.0:9502\n";
});

$server->on("open", function (Server $server, Request $request) {
    echo "connection open: {$request->fd}\n";
});

$server->on("message", function (Server $server, Frame $frame) {
    echo "received message: {$frame->data}\n";
    $server->push($frame->fd, "server: {$frame->data}");
});

$server->on("close", function (Server $server, int $fd) {
    echo "connection close: {$fd}\n";
});

$server->start();

协程编程

协程基础

php
<?php

use Swoole\Coroutine;
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;

$server = new Server("0.0.0.0", 9501);

$server->on("request", function (Request $request, Response $response) {
    // 创建协程
    Coroutine::create(function () use ($response) {
        // 模拟耗时操作
        Coroutine::sleep(1);
        $response->end("Hello from coroutine\n");
    });
});

$server->start();

协程MySQL

php
<?php

use Swoole\Coroutine\MySQL;

Coroutine::create(function () {
    $mysql = new MySQL();
    $mysql->connect([
        'host' => '127.0.0.1',
        'user' => 'root',
        'password' => 'root',
        'database' => 'test',
    ]);

    $result = $mysql->query('SELECT * FROM users');
    var_dump($result);
});

连接池

MySQL连接池

php
<?php

class MySQLPool
{
    private $pool = [];
    private $config;
    private $size;

    public function __construct($config, $size = 10)
    {
        $this->config = $config;
        $this->size = $size;
    }

    public function get()
    {
        if (empty($this->pool)) {
            $mysql = new Swoole\Coroutine\MySQL();
            $mysql->connect($this->config);
            return $mysql;
        }
        return array_pop($this->pool);
    }

    public function put($mysql)
    {
        if (count($this->pool) < $this->size) {
            array_push($this->pool, $mysql);
        }
    }
}

// 使用连接池
$pool = new MySQLPool([
    'host' => '127.0.0.1',
    'user' => 'root',
    'password' => 'root',
    'database' => 'test',
]);

Coroutine::create(function () use ($pool) {
    $mysql = $pool->get();
    $result = $mysql->query('SELECT * FROM users');
    $pool->put($mysql);
});

任务处理

异步任务

php
<?php

use Swoole\Server;

$server = new Server("127.0.0.1", 9503);

$server->set([
    'task_worker_num' => 4,
]);

$server->on("receive", function (Server $server, $fd, $reactor_id, $data) {
    // 投递异步任务
    $task_id = $server->task($data);
    $server->send($fd, "异步任务投递成功,task_id: {$task_id}\n");
});

$server->on("task", function (Server $server, $task_id, $reactor_id, $data) {
    // 处理异步任务
    echo "处理异步任务: {$data}\n";
    // 返回任务处理结果
    return "task {$task_id} 完成";
});

$server->on("finish", function (Server $server, $task_id, $data) {
    echo "异步任务完成: {$data}\n";
});

$server->start();

性能优化

内存管理

php
<?php

// 设置进程内存限制
ini_set('memory_limit', '256M');

// 定时清理内存
$server->tick(60000, function () {
    if (memory_get_usage() > 100 * 1024 * 1024) {
        gc_collect_cycles();
    }
});

进程管理

php
<?php

$server->set([
    'worker_num' => 4,        // 工作进程数
    'max_request' => 10000,   // 最大请求数
    'max_conn' => 10000,      // 最大连接数
]);

监控和日志

服务监控

php
<?php

use Swoole\Timer;

// 定时监控服务状态
Timer::tick(1000, function () use ($server) {
    $stats = $server->stats();
    echo "当前连接数: {$stats['connection_num']}\n";
    echo "接受连接数: {$stats['accept_count']}\n";
    echo "关闭连接数: {$stats['close_count']}\n";
    echo "工作进程数: {$stats['worker_num']}\n";
});

日志记录

php
<?php

function log($message, $level = 'info')
{
    $date = date('Y-m-d H:i:s');
    $pid = posix_getpid();
    file_put_contents(
        'swoole.log',
        "[{$date}] [{$level}] [{$pid}] {$message}\n",
        FILE_APPEND
    );
}

最佳实践

  1. 进程管理

    • 合理设置进程数
    • 及时回收资源
    • 避免内存泄漏
  2. 连接池管理

    • 合理设置池大小
    • 定时检查连接
    • 自动重连机制
  3. 错误处理

    • 全局异常处理
    • 日志记录
    • 监控告警
  4. 性能优化

    • 使用协程
    • 合理使用连接池
    • 异步任务处理

常见问题

  1. 内存泄漏

    • 定期清理内存
    • 避免循环引用
    • 及时释放资源
  2. 连接管理

    • 超时处理
    • 心跳检测
    • 自动重连
  3. 并发控制

    • 使用信号量
    • 原子操作
    • 锁机制

参考资料

  1. Swoole官方文档
  2. PHP手册
  3. 高性能服务器编程
  4. 协程编程指南
  5. 网络编程实践

幸运的人用童年治愈一生,不幸的人用一生治愈童年 —— 强爸