swoole8-信号驱动模型与event事件1
1. 信号模型
缺点
<?php namespace ShineYork\Io\Signal; // 这是等会自个要写的服务 class Worker { // 自定义服务的事件注册函数, // 这三个是闭包函数 public $onReceive = null; public $onConnect = null; public $onClose = null; // 连接 public $socket = null; public function __construct($socket_address) { $this->socket = stream_socket_server($socket_address); echo $socket_address."\n"; } // 需要处理事情 public function accept() { // 接收连接和处理使用 while (true) { $this->debug("accept start"); // 监听的过程是阻塞的 $client = stream_socket_accept($this->socket); pcntl_signal(SIGIO, $this->sigHander($client)); posix_kill(posix_getpid(), SIGIO); // 分发 pcntl_signal_dispatch(); $this->debug("accept end"); // 处理完成之后关闭连接 // 心跳检测 - 自己的心跳 // fclose($client); } } public function sigHander($client) { return function($sig) use ($client){ // is_callable判断一个参数是不是闭包 if (is_callable($this->onConnect)) { // 执行函数 ($this->onConnect)($this, $client); } $data = fread($client, 65535); if (is_callable($this->onReceive)) { ($this->onReceive)($this, $client, $data); } }; } public function debug($data, $flag = false) { if ($flag) { var_dump($data); } else { echo "==== >>>> : ".$data." \n"; } } // 发送信息 public function send($client, $data) { $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type: text/html;charset=UTF-8\r\n"; $response .= "Connection: keep-alive\r\n"; $response .= "Content-length: ".strlen($data)."\r\n\r\n"; $response .= $data; fwrite($client, $response); } // 启动服务的 public function start() { $this->accept(); } }信号I/O在大量IO操作时可能会因为信号队列溢出导致没法通知信号驱动I/O尽管对于处理UDP套接字来说有用,即这种信号通知意味着到达一个数据报,或者返回一个异步错误。但是,对于TCP而言,信号驱动的I/O方 式近乎无用,因为导致这种通知的条件为数众多,每一个来进行判别会消耗很大资源,与前几种方式相比优势尽失 4. event安装 由POSIX规范定义,应用程序告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到应用程序的缓冲区)完成后通知应用程序。这这种种模模型型与与信信号号驱驱动动模模型型的的主主要要区区别别在在于于::信信号号驱驱动动I/O是是由由内内核核 通通知知应应用用程程序序何何时时启启动动一一个个I/O操操作作,,而而异异步步I/O模模型型是是由由内内核核通通知知应应用用程程序序I/O操操作作何何时时完完成成 在学习之前我们需要使用到PHP中的一个扩展函数Event 系列-> 事件; 手册地址 https://php.golaravel.com/class.event.html 第一步先安装:event 注意当前我们 首先需要安装一下linux系统的libevent,然后再安装php7,-event扩展,素材均以准备好;
$ tar -zxvf libevent-2.1.8-stable.tar.gz $ cd libevent-2.1.8-stable $ ./configure --prefix=/usr/local/libevent-2.1.8 $ make $ make install $ ... $ tar -zxvf event-2.3.0.tgz $ cd event-2.3.0 $ phpize $ ./configure --with-php-config=/www/server/php/73/bin/php-config $ make $ make install $ php -m | grep event5. 使用event及坑 来个初体验把,这个函数呢 -》 其实是类似于laravel框架中的event;也就是事件操作区别就在于event是一个原生的依赖于libevent而实现的;不过这个函数的使用,资源很稀有; 这个函数就是PHP中的事件函数,而事件标签可以看手册了解 https://php.golaravel.com/event.flags.html 体验一下 : https://segmentfault.com/a/1190000016254243; 根据这个网址的案例及可以体验好
<?php $eventBase = new EventBase(); // 初始化一个定时器event(歼15,然后放到辽宁舰机库中) $timer = new Event( $eventBase, -1, Event::TIMEOUT | Event::PERSIST, function(){ echo microtime( true )." : 歼15,滑跃,起飞!".PHP_EOL; }); // tick间隔为0.05秒钟,我们还可以改成0.5秒钟甚至0.001秒,也就是毫秒级定时器 $tick = 0.05; // 将定时器event添加(将歼15拖到甲板加上弹射器) $timer->add( $tick ); // eventBase进入loop状态(辽宁舰!走你!) $eventBase->loop(); ?>接下来我们让event与socket结合;
?php $socket = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr); stream_set_blocking($socket, 0); $eventBase = new EventBase; $event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, function($socket) use ($eventBase) { echo "连接 start \n"; $conn = stream_socket_accept($socket); stream_set_blocking($conn, false); var_dump(fread($conn, 65535)); fwrite($conn, "hello event"); echo "连接 end \n"; }); $event->add(); $eventBase->loop(); ?>运行 php socket.php 既可以看到效果,不过这个event存在嵌套定义事件 的问题;比如现在需要针对于socket再定义一个write的事件;那么这个时候最好的办法就是代码如下的方式调整;
// server.php <?php $socket = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr); stream_set_blocking($socket, 0); $eventBase = new EventBase; $event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, function($socket) use ($eventBase) { echo "连接 start \n"; $conn = stream_socket_accept($socket); stream_set_blocking($conn, false); $event = new Event($eventBase, $conn, Event::WRITE | Event::PERSIST, function($conn) use ($eventBase) { echo "WRITE start \n"; var_dump(fread($conn, 65535)); fwrite($conn, "hello event"); echo "WRITE end \n"; }); $event->add(); echo "连接 end \n"; }); $event->add(); $eventBase->loop(); ?>可以看到效果是不行的;这个和event这个对象有关系,因为它在同作用域下在重新定义不生效避免覆盖原有的event;因此我们需要做一个额外操作;可以创建另一个文件 比如E.php,还有为了与生效我们还需要额外的 在server.php中定义一个额外的参数作为记录,属性名不要管event类需要
e.php <?php use \Event as Event; class e { protected $client; protected $eventBase; function __construct($eventBase, $client, &$count) { $this->eventBase = $eventBase; $this->client = $client; } public function handler() { $event = new Event($this->eventBase, $this->client, Event::PERSIST |Event::READ | Event::WRITE , function($socket){ // 对于建立处理时间 var_dump(fread($socket, 65535)); fwrite($socket, " 提前祝大家平安夜快乐 \n"); fclose($socket); ($this->count[(int) $socket][Event::PERSIST | Event::READ | Event::WRITE])->free(); }); $event->add(); $this->count[(int) $this->client][Event::PERSIST | Event::READ | Event::WRITE] = $event; var_dump($this->count); } } ?> eventSocket.php <?php require 'e.php'; use \Event as Event; use \EventBase as EventBase; $socket_address = "tcp://0.0.0.0:9000"; $server = stream_socket_server($socket_address); echo $socket_address."\n"; $eventBase = new EventBase(); // 记录我们所创建的这样事件 让 $eventBase 可以找到这个事件 $count = [];// 变量没有要求 随便 $event = new Event($eventBase, $server, Event::PERSIST | Event::READ | Event::WRITE , function($socket) use ($eventBase, &$count){ // 在闭包中的 function($socket) 的$socket 就是 // 在构造函数中传递的 $server 这个属性 // 也就是 $socket = $server // 建立与用户的连接 echo "连接 start \n"; $client = stream_socket_accept($socket); (new E($eventBase, $client, $count))->handler(); echo "连接 end \n"; }); $event->add(); $count[(int) $server][Event::PERSIST | Event::READ | Event::WRITE] = $event; var_dump($count); $eventBase->loop(); ?>