swoole9-回顾event及实现异步io模型
0. 额外参照
为了与方便操作,我们定义一个该组件的助手函数方式,只需要在composer.json中增加一项操作即可
{ "name": "shineyork/io", "type": "library", "license": "MIT", "authors": [ { "name": "shineyork", "email": "shineyork@sixstaredu.com" } ], "autoload":{ "psr-4":{ "ShineYork\\Io\\":"./src/" }, "files":[ "src/Helper.php" ] }, "require": {} }2. 使用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结合;
// socket.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); var_dump(fread($conn, 65535)); fwrite($conn, "hello event"); echo "连接 end \n"; }); $event->add(); $eventBase->loop();运行 php socket.php 既可以看到效果,不过这个event存在嵌套定义事件 的问题;比如现在需要针对于socket再定义一个write的事件;那么这个时候最好的办法就是代码如下的方式调整 // server.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类需要 4. Reactor理解 4.1. 传统的方式 特点
- 采用阻塞式I/O模型获取输入数据
- 每个连接都需要独立的线程完成数据输入,业务处理,数据返回的完整操作
- 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大
- 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在read操作上,造成线程资源浪费
- Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应。 它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人
- Handlers处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作
- Reactor对象通过select监控客户端请求事件,收到事件后通过dispatch进行分发
- 如果是建立连接请求事件,则由Acceptor通过accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理
- 如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应
- Handler会完成read->业务处理->send的完整业务流程
- 性能问题:只有一个线程,无法完全发挥多核CPU的性能Handler在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈
- 可靠性问题:线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障