我是早就看过这篇基于 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 改写一个支持数据库连接池的版本。