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操作上,造成线程资源浪费
4.2. Reactor模式   Reactor模式,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。 服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor模式也叫Dispatcher模式,即I/O多了 复用统一监听事件,收到事件后分发(Dispatch给某进程),是编写高性能网络服务器的必备技术之一   Reactor模式中有2个关键组成:
  • Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应。 它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人
  • Handlers处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作
5. 单Reactor单线程   方案说明
  • Reactor对象通过select监控客户端请求事件,收到事件后通过dispatch进行分发
  • 如果是建立连接请求事件,则由Acceptor通过accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理
  • 如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应
  • Handler会完成read->业务处理->send的完整业务流程
优点   模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成   缺点
  • 性能问题:只有一个线程,无法完全发挥多核CPU的性能Handler在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈
  • 可靠性问题:线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障
使用场景   客户端的数量有限,业务处理非常快速,比如Redis,业务处理的时间复杂度O(1) 而我们之前的模型则就是单Reactor单线程这样的模型; 注意在我们的代码中因为没有直接去调用dispatch方法,因为我们使用的是swoole中的event在进行add的时候已经处理了:这里分别把swoole的方式及使用PHP中 event扩展的方式实现的单Reactor给大家''''