MENU

绝杀——Swoole、PHP与MySQL:连接池

February 3, 2014 • 程序阅读设置

我是早就看过这篇基于swoole扩展实现真正的PHP数据库连接池了。看完了,觉得真的很厉害。毕竟,现在来说可能是还没有真正在PHP使用数据库连接池的大应用。今天终于准备实验实验。

在开始之前,还是说一下测试环境吧:OS CentOS 6.4 x86;php 5.3.17;MySQL 5.5.28;Swoole 1.6.8。

首先,我们当然要把代码写好。下面是服务端代码,很多是参考了上面文章里面的,当然也有自己的内容。

<?php
$serv = swoole_server_create('127.0.0.1', 3305, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);//端口3305
swoole_server_set($serv, array(
    'worker_num' => 2,      //worker线程的数量
    'task_worker_num' => 1, //MySQL连接的数量
));//作为小型测试,参数调得比较小
//这里有一个守护进程化的参数,由于是实验,没有加入
function my_onReceive($serv, $fd, $from_id, $data){
    //执行查询
    $result = $serv->taskwait($data);
    if ($result !== false) {
        swoole_server_send($serv, $fd, $result);
        return;
    } else {
        swoole_server_send($serv, $fd, "Error. Task timeout\n");
    }
}
function my_onTask($serv, $task_id, $from_id, $sql){
    static $link = NULL;
    if ($link == NULL) {
        $link = mysqli_connect('localhost', 'user', 'pw', 'db');
        //localhost=>UNIX Socket , IP地址=>TCP/IP
    }
    $result = $link->query($sql);
    if ($result === false) {
        swoole_server_finish($serv, 'b:0;');//语句运行失败,这是serialize后的false,下同理
        return;
    }
    if ($result === true){
        swoole_server_finish($serv, 'b:1;');//写入操作成功
        return;
    }
    $data = $result->fetch_all(MYSQLI_ASSOC);
    swoole_server_finish($serv, serialize($data));
}
function my_onFinish($serv, $data){
    //这次实验就没有写东西了
}
swoole_server_handler($serv, 'onReceive', 'my_onReceive');
swoole_server_handler($serv, 'onTask', 'my_onTask');
swoole_server_handler($serv, 'onFinish', 'my_onFinish');
//上面是设置回调函数
swoole_server_start($serv);
swoole_event_wait();//实验环境是PHP5.3,所以需要这个函数进行事件轮询;5.4+就不需要了

如果把上面的连接池代码和Rango的相比较,会发现我的对于错误的部分处理很少。其实这个时候用CLI运行这个连接池,然后使用Telnet已经就可以直接测试效果了。但是不管怎么说这个东西还是要应用在PHP上面的,我简单地写一下PHP方面的代码。

<?php
$link = new swoole_client(SWOOLE_SOCK_TCP,SWOOLE_SOCK_SYNC);//TCP方式、同步
$link->connect('127.0.0.1', 3305);//连接
$link->send('SELECT * FROM `table`;');//执行查询
$res = unserialize($link->recv());
if(!res) {
    echo 'Failed!';
}
else {
    print_r($res);
}
$link->close();

//上面的是最简单的测试,下面可以简单地改写成函数

function dbcp_query($sql) {
    $link = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);//TCP方式、同步
    $link->connect('127.0.0.1', 3305);//连接
    $link->send($sql);//执行查询
    return unserialize($link->recv());
    //swoole_client类析构时会自动关闭连接
}

现在可以运行了,本次实验是成功的。但是如果使用dbcp_query()这个函数,每次调用都要发起一次TCP连接,执行的语句多了,肯定出问题。这个时候我们就可以把它封装成一个类了,单纯实现这个会比较的简单,但是打出来要点时间,这里就不写了。

最后:今天做的是数据库连接池的实现。从上面的代码我们可以看见,程序与连接池之间的数据交换是使用php序列进行的。这里会有两次的serialize、unserialize,绝对也是一个开销。Rango的文章里面有说到“MySQL是每个连接会占用1个线程……大量的系统资源被浪费在线程间上下文切换上……不是所有地方都在做数据库操作,所以这个就是浪费的。”再看看他那篇文章的假设:“假设有100台PHP的应用服务器,每个机器需要启动100个apache或fpm工作进程。”这肯定不是一个小项目,确实就适合用连接池了。写的东西是用来练手或者解闷儿的?常规方法已经可以了。不要忘了一点:程序与连接池的交互我们应该还是用Swoole实现的,Swoole可是一个TCP/UDP扩展。而Swoole只能运行在Linux平台上面,但是Linux平台上的MySQL是可以用UNIX Socket通讯的。

P.S.:找个时间给epdb改写一个支持数据库连接池的版本。

Archives Tip
QR Code for this page
Tipping QR Code