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在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈
- 可靠性问题:线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障