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