http://rango.swoole.com/archives/category/php_swoole
‘Swoole扩展’ 分类的存档
关于C++、PHP和Swoole
2015年8月25日 韩 天峰 评论已被关闭
昨天和一个前同事聊天,各种吐槽PHP,吐槽Swoole,他认为PHP到处是坑,PHP局限很大。PHP+Swoole不适合做高并发服务器,C+Swoole才是最好的方案。C++有各种数据结构,C++可以开线程,C++可以共享对象。看来有必要好好得说明一下了。
PHP比C/C++或Java少了什么?多线程,多线程,多线程……
是的。PHP比C/C++、Java少了多了多线程。PHP只有多进程的方案,所以PHP里的全局变量和对象不是共享的、数据结构也不能跨进程操作、Socket文件描述符不能共享等等。所以PHP有局限?
多线程看似比多进程要强大很多,实际上我可以负责任的告诉你,多线程带来的坑更多。
- 数据同步问题会让你崩溃的。要么就牺牲性能到处加锁,要么就用地狱难度的无锁并发编程,据我所知目前国内能掌握此项技能的人凤毛麟角。
- 不要以为加锁就万事大吉了,你会在死锁问题上栽个大跟头。当你的程序逻辑复杂后,锁越来越难控制了,一旦死锁你的程序基本上就完了。
- 某个线程挂了那所有线程都会退出
反而在看多进程,其实就简单的多了。
- 配合进程间通信,基本上你可以实现任意的数据共享。比如利用一个进程专门存数据结构和对象,其他进程的数据操作全部投递到此进程来
- 多进程不需要锁
- 多进程可以使用共享内存的数据结构实现一些多线程的功能。如Swoole提供的Table、Atomic可以实现数据共享,但成本很低。未来还会加入共享内存队列
所谓PHP限制了Swoole,这完全是无稽之谈。合理利用Swoole提供的Table、Atomic、SendMessage/PipeMessage、Task完全可以实现异步非阻塞的代码逻辑。
C++写出来的程序性能更好?
这完全是盲目的迷信,密集计算的程序C++确实是有优势的。而并发服务器核心是IO,并非大规模密集运算。C++从语言层面来看并没有什么优势。另外C++中的大部分数据结构在PHP中都有对应的实现,实在不行自己写个专门的扩展也能解决之。
高并发的服务器单机能维持10W连接、每秒可处理3-5W笔消息收发。这种性能水准已经可以应用在BAT的核心系统上了。
开发效率快的意义是什么?
这位同事还说PHP开发Server虽然比C++快了,但是追求性能的极致还是要用C++。我要告诉你效率高了究竟意义何在。开发一套好程序不是一件容易的事情,需要程序员投入大量时间和精力。开发效率提升的意义并不是简单的我可以更少时间完工,而是剩下的时间你可以增加单元测试、修复BUG、提升用户体验、完善细节、提供配套工具、优化性能、增加关键日志、增加监控报警、增加容灾方案。
关于TCP网络通信
2015年8月10日 韩 天峰 评论已被关闭
TCP协议在底层机制上解决了UDP协议的顺序和丢包重传问题。但相比UDP又带来了新的问题,TCP协议是流式的,数据包没有边界。应用程序使用TCP通信就会面临这些难题。一些程序在本机测试是正确的,上线后就出现各种奇怪的BUG。如下面的伪代码,客户端向服务器端发送一个json字符串,服务器端接收此字符串。在慢速网络中Server无法正确接收完整的JSON字符串。
$client->send(json_encode('a' => $data_10k, 'b' => $data_5k));
$pkg = $server->recv(); //Server收到的数据只有一小部分
$client->send("hello1");
$client->send("hello2");
$client->send("hello3");
$pkg = $server->recv(); //Server会一次性收到3个数据包
因为TCP通信是流式的,在接收1个大数据包时,可能会被拆分成多个数据包发送。多次Send底层也可能会合并成一次进行发送。这里就需要2个操作来解决:
- 分包:Server收到了多个数据包,需要拆分数据包
- 合包:Server收到的数据只是包的一部分,需要缓存数据,合并成完整的包
具体编码实现这里就不讲了,这是一个比较复杂的编程过程,稍有不慎就会出现严重的BUG
Swoole如何解决此问题
在swoole中可以使用Length/EOF 2种协议解析方式来完美解决此问题。从1.7.18版本开始,Swoole的Server/Client都支持了Length/EOF的协议处理方式,应用层代码只需要配置一下参数,就无需关注底层分包合包了。每次onReceive收到的数据包总是完整的。
Server/Client的配置是相同的
EOF
$server->set(array('open_eof_split' => true, 'package_eof' => "\r\n"));
EOF协议处理的原理是每个数据包结尾加一串特殊字符表示包已结束。如memcache、ftp、stmp都使用\r\n作为结束符。发送数据时只需要在包末尾增加\r\n即可。使用EOF协议处理,一定要确保数据包中间不会出现EOF,否则会造成分包错误。
Length
$server->set(array(
'open_length_check' => true,
'package_max_length' => 81920,
'package_length_type' => 'n', //see php pack()
'package_length_offset' => 0,
'package_body_offset' => 2,
));
固定包头的协议非常通用,在BAT的服务器程序中经常能看到。这种协议的特点是一个数据包总是由包头+包体2部分组成。包头由一个字段指定了包体或整个包的长度,长度一般是使用2字节/4字节整数来表示。服务器收到包头后,可以根据长度值来精确控制需要再接收多少数据就时完整的数据包。Swoole的配置可以很好的支持这种协议,可以灵活地设置4项参数应对所有情况。
Swoole的Server和异步Client都是在onReceive回调函数中处理数据包,当设置了协议处理后,只有收到一个完整数据包时才会触发onReceive事件。同步客户端在设置了协议处理后,调用 $client->recv() 不再需要传入长度,recv函数在收到完整数据包或发生错误后返回。
swoole新增的tick定时器
2015年3月24日 韩 天峰 评论已被关闭
swoole-1.7.14增加了tick定时器,解决了addtimer存在的各种问题。
swoole_timer_tick(1000, function ($timer_id) {
echo "tick-1000ms\n";
swoole_timer_clear($timer_id);
})
swoole_timer_tick(2000, function () {
echo "tick-2000ms\n";
})
swoole_timer_tick和swoole_timer_after原理是相同的,区别是after只执行一次就会销毁。tick定时器会持续执行,直到调用了swoole_timer_clear清除定时器。
与PHP代码中的pcntl_alarm不同,swoole的定时器是基于EventLoop实现的,不依赖系统信号和PHP的tick机制,所以效率非常高。即使添加大量定时器,CPU消耗也是很低的。PHP在过去没有像JavaScript这样方便的定时器功能,现在有swoole就可以很好地解决。
在网络游戏程序中经常要用定时器,PHP+swoole将会成为最佳的技术方案。
分类: Swoole扩展 标签:
PHP官方的pcntl_signal性能极差
很多纯PHP开发的后端框架中都使用了pcntl扩展提供的信号处理函数pcntl_signal,实际上这个函数的性能是很差的。首先看一段示例代码:
declare(ticks = 1);
pcntl_signal(SIGINT, 'signalHandler');
这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。因为PHP的函数无法直接注册到操作系统信号设置中,所以pcntl信号需要依赖tick机制。通过查看pcntl.c的源码实现发现。pcntl_signal的实现原理是,触发信号后先将信号加入一个队列中。然后在PHP的ticks回调函数中不断检查是否有信号,如果有信号就执行PHP中指定的回调函数,如果没有则跳出函数。
PHP_MINIT_FUNCTION(pcntl)
{
php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
php_add_tick_function(pcntl_signal_dispatch TSRMLS_CC);
return SUCCESS;
}
pcntl_signal_dispatch 函数的实现:
void pcntl_signal_dispatch()
{
//.... 这里略去一部分代码,queue即是信号队列
while (queue) {
if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
ZVAL_NULL(&retval);
ZVAL_LONG(¶m, queue->signo);
/* Call php signal handler - Note that we do not report errors, and we ignore the return value */
/* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
call_user_function(EG(function_table), NULL, handle, &retval, 1, ¶m TSRMLS_CC);
zval_ptr_dtor(¶m);
zval_ptr_dtor(&retval);
}
next = queue->next;
queue->next = PCNTL_G(spares);
PCNTL_G(spares) = queue;
queue = next;
}
}
这样就存在一个比较严重的性能问题,大家都知道PHP的ticks=1表示每执行1行PHP代码就回调此函数。实际上大部分时间都没有信号产生,但ticks的函数一直会执行。如果一个服务器程序1秒中接收1000次请求,平均每个请求要执行1000行PHP代码。那么PHP的pcntl_signal,就带来了额外的 1000 * 1000,也就是100万次空的函数调用。这样会浪费大量的CPU资源。
比较好的做法是去掉ticks,转而使用pcntl_signal_dispatch,在代码循环中自行处理信号。而swoole中因为底层是C实现的,信号处理不受PHP的影响。swoole使用了目前Linux系统中最先进的signalfd来处理信号,几乎是没有任何额外消耗的。
分类: PHP系统编程, Swoole扩展, 进程间通信 标签:
swoole与phpdaemon/reactphp/workerman等纯PHP网络库的差异
从swoole项目开始到现在,一直有人在问这个问题。今天来抽空讲一下它。为什么swoole非要使用纯C来写而不是PHP代码来实现,核心的原因有2点:
1. PHP无法直接调用操作系统API
如sendfile、eventfd、timerfd、pthread等等,这里就不一一列举了,所以纯PHP实现的phpdaemon,reactphp,还有最近刚刚出来的workerman。这些框架都是基于PHP的sockets/pcntl/stream/libevent扩展实现,提供的功能很有限,很多功能都无法实现。如
- 多线程
- 毫秒定时器(PHP中只有秒级的定时器)
- 标准输入输出重定向
- MySQL/CURL异步化
- 守护进程化
- sendfile
- 其他更多
而C语言写的swoole可以直接调用操作系统底层API,没有局限,swoole可以实现任何功能特性。
2. PHP的内存管理粒度太粗
PHP中想要做内存管理太困难了,基本上只有Array可用。在高并发大负载的网络Server中,内存复制简直就是性能杀手。PHP中根本无法解决此问题。
举一个简单的例子,客户端向服务器发起一个800K的包,每次发送8K,共发送100次。Server也会分成100次收到数据。那么PHP中拼接此数据包的方法是 $package .= $recv_data 。共需要复制100次内存,第一次为 8K+ 8K,第二次是 16K + 8K,第三次24K + 8K,依次类推,仅仅一次请求就发生了大量的内存拷贝。如果每秒有10万次请求,这个Server的性能必然极差。
而纯C的代码可以做到0次内存拷贝,在请求到来申请一块800K的buffer内存,通过指针运算,直接将数据写入buffer。一气呵成,内存拷贝为0。
当然这里仅是其中一个小小的点,真正的代码中不止这些。通过压测也能发现,纯C的swoole写一个EchoServer,做-c 500 -n 100000的测试中,CPU始终在5%-10%之间。而PHP实现的PSF网络Server框架,CPU占用率高达70%-90%。
以上也就是swoole和其他网络框架的差异。除此之外swoole以扩展方式提供,免去了代码中include php文件的问题。不需要去包含一堆外部文件,更容易融合到现有代码中。使用者仅需掌握swoole扩展的API即可。reactphp提供了API封装,耦合程度较低。phpdaemon/workerman耦合太高,不是你的代码集成它们,而是它们的代码集成你的代码。而且还需要了解其内部结构和耦合关系。
再看swoole,它其实就像MySQL之类的扩展一样,仅仅是作为一层API存在,耦合度非常低。swoole一直坚持低耦合高内聚,API化。用户可以方便的将swoole的功能集成到自己的代码中。
分类: Swoole扩展 标签:
node.js与swoole相比优势与劣势分析
这几天微博上好多网友在询问node.js和swoole对比的问题。这里做一个详细的解答。
多核并行
node.js的event loop是单进程单线程的,只有一个epoll/kqueue事件轮询被执行。所以无法利用到多核的计算优势。
swoole的event loop是多线程的,是基于epoll/kqueue的Multi-Reactor模型。这点与nginx/golang相同。另外swoole的多线程Reactor之间不存在锁,这点与nginx不同。它启用了专门的accept线程,类似与JAVA的netty。
所以在多核CPU的机器上,node.js对网络IO事件的处理能力绝对是要差swoole数倍的,在4核的机器上至少要查2-3倍。
虽然node.js也提供了多线程的扩展,但对于event_loop来说必须是内核提供,扩展不行的。
另外node.js未来是否会改造成为多线程Reactor,我估计不会,这不是技术上的难题。而是一旦改成多线程Reactor,node.js恐怕就不是node.js了。失去了原来单线程的各种优势后,反而会一无是处。当然可能node.js官方开发组会思考出解决问题的巧妙办法,一切都是后话了。
不只是event_loop,执行用户层代码是也是单线程的,不能利用多核计算优势。需要用户自己去fork多进程或者创建线程池。使用难度增加了很多。不像swoole,配置一下参数即可,是天然多进程。
异步网络IO
node.js和swoole都是基于epoll/kqueue实现的全异步非阻塞IO,所以这方面大同小异,没有差别。维持TCP长连接的能力是一样的。
node.js在这里有一个优势就是它支持windows的IOCP。swoole仅支持Linux/FreeBSD/MacOS的epoll/kqueue.
异步文件读写
node.js和swoole都提供了基于线程池的异步文件读写,DNS查询功能。node.js最早基于libeio实现,后来才自行实现线程池。swoole一开始就基于线程池设计的。
实际上这里都是通过多线程阻塞来模拟的,并非真正的异步读写文件。比如同时读写数百个文件时,性能远不如普通的多进程PHP程序。
swoole中还提供了Linux Native AIO的支持,是真正的内核层异步并行文件读写,不过需要通过修改宏开关,重新编译才可以使用。
最后结论
node.js和swoole比没有明显优势,仅在Windows支持方面比swoole要好。node.js中有的特性swoole中都有。个人认为Node.js最大的优势在于:
- node.js使用JavaScript语言,与浏览器、HTML之间的融合度非常高,使用同一种语言既写前端又写后端
- 支持Windows平台,利用node-webkit,可以开发PC客户端软件
node.js的定位应该是前端与后端结合非常紧密的应用场景。如websocket推送,JSON-RPC,轻量级HTTP接口。
而对于真正专业的后端领域,分布式系统,node.js不适合。
分类: Node.js, PHP, Swoole扩展 标签: Node.js
swoole的进程模型架构
swoole的强大之处就在与其进程模型的设计,既解决了异步问题,又解决了并行。
主线程MainReactor
swoole启动后主线程会负责监听server socket,如果有新的连接accept,主线程会评估每个Reactor线程的连接数量。将此连接分配给连接数最少的reactor线程。这样的好处是
- 每个reactor线程持有的连接数是非常均衡的,没有单个线程负载过高的问题
- 解决了惊群问题,尤其是拥有多个listen socket时,节约了线程唤醒和切换的开销
主线程内还接管了所有信号signal的处理,使Reactor线程运行中可以不被信号打断。
管理进程Manager
swoole运行中会创建一个单独的管理进程,所有的worker进程和task进程都是从管理进程Fork出来的。管理进程会监视所有子进程的退出事件,当worker进程发生致命错误或者运行生命周期结束时,管理进程会回收此进程,并创建新的进程。
管理进程还可以平滑地重启所有worker进程,以实现程序代码的重新加载。
异步Reactor线程
swoole拥有多线程Reactor,所以可以充分利用多核,开启CPU亲和设置后,Reactor线程还可以绑定单独的核,节约CPU Cache开销。
swoole的Reactor线程是全异步非阻塞的,即使你的worker进程用了同步模式,依然不影响reactor线程的性能。在worker进程组很繁忙的状况下,reactor线程完全不受影响,依然可以收发处理数据。
TCP是流式的,没有边界,所以处理起来很麻烦。Reactor线程可以根据EOF或者包头长度,自动缓存数据,组装数据包。等一个请求完全收到后,再投递给Worker进程。
同步或异步Worker进程
与传统的半同步半异步服务器不同,Swoole的worker进程可以是同步的也可以异步的,这样带来了最大的灵活性。当你的Server需要很高性能,业务逻辑较为简单时你可以选择异步模式。当业务逻辑复杂多变,可以选择同步模式。
这里要比Node.js强大太多了。
TaskWorker进程池
swoole除了Reactor线程,Worker进程外还提供了TaskWorker进程池,目的是为了解决在业务代码中,有些逻辑部分不需要马上执行。利用task进程池,可以方便的投递一个异步任务去执行,在Worker进程空闲时再去捕获任务执行的结果。
PHP开发异步高性能的MySQL代理服务器
MySQL数据库对每个客户端连接都会分配一个线程,所以连接非常宝贵。开发一个异步的MySQL代理服务器,PHP应用服务器可以长连接到这台Server,既减轻MYSQL的连接压力,又使PHP保持长连接减少connect/close的网络开销。
此Server考虑到了设置了数据库连接池尺寸,区分忙闲,mysqli断线重连,并设置了负载保护。基于swoole扩展开发,io循环使用epoll,是全异步非阻塞的,可以应对大量TCP连接。
程序的逻辑是:启动时创建N个MySQL连接,收到客户端发来的SQL后,分配1个MySQL连接,将SQL发往数据库服务器。然后等待数据库返回查询结果。当数据库返回结果后,再发给对应的客户端连接。
核心的数据结构是3个PHP数组。idle_pool是空闲的数据库连接,当有SQL请求时从idle_pool中移到busy_pool中。当数据库返回结果后从busy_pool中再移到idle_pool中,以供新的请求使用。当SQL请求到达时如果没有空闲的数据库连接,那会自动加入到wait_queue中。一旦有SQL完成操作,将自动从wait_queue中取出等待的请求进行处理。
如此循环使用。由于整个服务器是异步的单进程单线程所以完全不需要锁。而且是完全异步的,效率非常高。
当然本文的代码,如果要用于生产环境,还需做更多的保护机制和压力测试。在此仅抛砖引玉,提供一个解决问题的思路。
class DBServer
{
protected $pool_size = 20;
protected $idle_pool = array(); //空闲连接
protected $busy_pool = array(); //工作连接
protected $wait_queue = array(); //等待的请求
protected $wait_queue_max = 100; //等待队列的最大长度,超过后将拒绝新的请求
/**
* @var swoole_server
*/
protected $serv;
function run()
{
$serv = new swoole_server("127.0.0.1", 9509);
$serv->set(array(
'worker_num' => 1,
));
$serv->on('WorkerStart', array($this, 'onStart'));
//$serv->on('Connect', array($this, 'onConnect'));
$serv->on('Receive', array($this, 'onReceive'));
//$serv->on('Close', array($this, 'onClose'));
$serv->start();
}
function onStart($serv)
{
$this->serv = $serv;
for ($i = 0; $i < $this->pool_size; $i++) {
$db = new mysqli;
$db->connect('127.0.0.1', 'root', 'root', 'test');
$db_sock = swoole_get_mysqli_sock($db);
swoole_event_add($db_sock, array($this, 'onSQLReady'));
$this->idle_pool[] = array(
'mysqli' => $db,
'db_sock' => $db_sock,
'fd' => 0,
);
}
echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n";
}
function onSQLReady($db_sock)
{
$db_res = $this->busy_pool[$db_sock];
$mysqli = $db_res['mysqli'];
$fd = $db_res['fd'];
echo __METHOD__ . ": client_sock=$fd|db_sock=$db_sock\n";
if ($result = $mysqli->reap_async_query()) {
$ret = var_export($result->fetch_all(MYSQLI_ASSOC), true) . "\n";
$this->serv->send($fd, $ret);
if (is_object($result)) {
mysqli_free_result($result);
}
} else {
$this->serv->send($fd, sprintf("MySQLi Error: %s\n", mysqli_error($mysqli)));
}
//release mysqli object
$this->idle_pool[] = $db_res;
unset($this->busy_pool[$db_sock]);
//这里可以取出一个等待请求
if (count($this->wait_queue) > 0) {
$idle_n = count($this->idle_pool);
for ($i = 0; $i < $idle_n; $i++) {
$req = array_shift($this->wait_queue);
$this->doQuery($req['fd'], $req['sql']);
}
}
}
function onReceive($serv, $fd, $from_id, $data)
{
//没有空闲的数据库连接
if (count($this->idle_pool) == 0) {
//等待队列未满
if (count($this->wait_queue) < $this->wait_queue_max) {
$this->wait_queue[] = array(
'fd' => $fd,
'sql' => $data,
);
} else {
$this->serv->send($fd, "request too many, Please try again later.");
}
} else {
$this->doQuery($fd, $data);
}
}
function doQuery($fd, $sql)
{
//从空闲池中移除
$db = array_pop($this->idle_pool);
/**
* @var mysqli
*/
$mysqli = $db['mysqli'];
for ($i = 0; $i < 2; $i++) {
$result = $mysqli->query($sql, MYSQLI_ASYNC);
if ($result === false) {
if ($mysqli->errno == 2013 or $mysqli->errno == 2006) {
$mysqli->close();
$r = $mysqli->connect();
if ($r === true) continue;
}
}
break;
}
$db['fd'] = $fd;
//加入工作池中
$this->busy_pool[$db['db_sock']] = $db;
}
}
$server = new DBServer();
$server->run();
swoole的异步和同步
有人问node.js和swoole有何不同,最大的不同当然是语言了,一个是Javascript一个是PHP。除此之外最大的不同是,swoole不仅支持异步,还支持同步。而node.js只支持异步,代码中不能有任何阻塞,否则程序就会变得效率很差。
swoole如果选择异步模式,所有网络连接应当使用swoole_client。其他数据库查询,API调用都应当使用swoole_event_add将socket加入到异步事件循环中。一些必须要阻塞的操作就用swoole提供的task/finish功能进行异步化。异步方式单个worker进程的程序效率很高,不存在IOWait,所以一个进程就能同时处理成千上万的请求,只需要开启CPU核数的1倍或2倍的worker进程即可。
如果选择阻塞模式,那在你的代码中就像php-fpm或mod_php中那样编码即可。另外根据系统的负载情况设定足够多的worker进程。比如每个请求需要50ms,那单个worker进程只能达到20QPS。启动100个worker进程就可以有2000QPS的处理能力。当然进程越多那浪费在进程间切换的CPU时间就会越多,系统的效率越来越差。
有人还提出假如Server的处理能力是100QPS,那同时并发10000个请求怎么办?这个问题就像银行窗口一样,假如他们的处理能力是每小时100个客户,假设来了1万个人,那只能排队等候处理或者丢弃一部分人。Server也是一样,超过处理能力的请求都会排队放在socket缓冲区中,如果socket缓冲区也满了,那新的请求就将丢弃。
基于swoole扩展实现真正的PHP数据库连接池
PHP的数据库连接池一直以来都是一个难题,很多从PHP语言转向Java的项目,大多数原因都是因为Java有更好的连接池实现。PHP的MySQL扩展提供了长连接的API,但在PHP机器数量较多,规模较大的情况下,mysql_pconnect非但不能节约MySQL资源,反而会加剧数据库的负荷。
假设有100台PHP的应用服务器,每个机器需要启动100个apache或fpm工作进程,那每个进程都会产生一个长连接到MySQL。这一共会产生1万个My SQL连接。大家都知道MySQL是每个连接会占用1个线程。那MYSQL就需要创建1万个线程,这样大量的系统资源被浪费在线程间上下文切换上。而你的业务代码中并不是所有地方都在做数据库操作,所以这个就是浪费的。
连接池就不同了,100个worker进程,公用10个数据库连接即可,当操作完数据库后,立即释放资源给其他worker进程。这样就算有100台PHP的服务器,那也只会创建1000个MySQL的连接,完全可以接受的。
以前确实没有好的办法来解决此问题的,现在有了swoole扩展,利用swoole提供的task功能可以很方便做出一个连接池来。
代码如下:
$serv = new swoole_server("127.0.0.1", 9508);
$serv->set(array(
'worker_num' => 100,
'task_worker_num' => 10, //MySQL连接的数量
));
function my_onReceive($serv, $fd, $from_id, $data)
{
//taskwait就是投递一条任务,这里直接传递SQL语句了
//然后阻塞等待SQL完成
$result = $serv->taskwait("show tables");
if ($result !== false) {
list($status, $db_res) = explode(':', $result, 2);
if ($status == 'OK') {
//数据库操作成功了,执行业务逻辑代码,这里就自动释放掉MySQL连接的占用
$serv->send($fd, var_export(unserialize($db_res), true) . "\n");
} else {
$serv->send($fd, $db_res);
}
return;
} else {
$serv->send($fd, "Error. Task timeout\n");
}
}
function my_onTask($serv, $task_id, $from_id, $sql)
{
static $link = null;
if ($link == null) {
$link = mysqli_connect("127.0.0.1", "root", "root", "test");
if (!$link) {
$link = null;
$serv->finish("ER:" . mysqli_error($link));
return;
}
}
$result = $link->query($sql);
if (!$result) {
$serv->finish("ER:" . mysqli_error($link));
return;
}
$data = $result->fetch_all(MYSQLI_ASSOC);
$serv->finish("OK:" . serialize($data));
}
function my_onFinish($serv, $data)
{
echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL;
}
$serv->on('Receive', 'my_onReceive');
$serv->on('Task', 'my_onTask');
$serv->on('Finish', 'my_onFinish');
$serv->start();
这里task_worker_num就是要启用的数据库连接池数量,worker进程为100时,连接池数量为10就可以设置为worker_num = 100, task_worker_num = 10。