在WorkerMan源码分析 - 实现最简单的原型文章中提到了libevent网络库,前者和libev都是事件驱动编程库高性能。简单说libev对libevent做了改进和精简。libevent使用全局变量,导致非线程安全。它的watcher结构也过大,把I/O、计时器、信号句柄整合在一起。而且(作者认为)libevent的附加组件如http和dns库都实现不好,且有安全问题。libev对这些地方做了改进,避免使用全局变量,拆分watcher等。另外libev去掉了外部库(比如http和dns),只专注于事件驱动服务,所以更精简。
安装libev扩展
地址:https://pecl.php.net/package/ev
实例
定时器实现
<?php //定时器的实现,2秒后每2秒触发1次注册的事件 $loop = new EvLoop(); function timers() { echo date("Y-m-d H:i:s") . "\n"; } //$event = $loop->timer(2,0,'timers'); $event = $loop->timer(2,2,'timers'); $loop->run();
结果如下:
2018-04-25 16:57:54
2018-04-25 16:57:55
2018-04-25 16:57:56
2018-04-25 16:57:57
基于事件循环 异步io 的socket读写
1 <?php 2 $loop = new EvLoop(); 3 $arr = []; 4 $data = "hello world\n"; 5 6 function add($fd,$ev,$func){ 7 global $arr,$loop; 8 $func = getStreamListenerClosure($fd,$func); 9 //创建观察者监听io $ev事件 10 $event = $loop->io($fd,$ev,$func); 11 $arr[posix_getpid()][(int)$fd] = $event; 12 } 13 14 //返回闭包 15 function getStreamListenerClosure($stream, $listener){ 16 return function () use ($stream,$listener){ 17 call_user_func($listener,$stream); 18 }; 19 } 20 21 //读 22 function baseRead($socket){ 23 $buffer = @fread($socket, 2); 24 25 if ($buffer == "\n"){ 26 //结尾 27 fclose($socket) && removeStream($socket); 28 } else { 29 echo $buffer; 30 } 31 } 32 33 //写 34 function baseWrite($socket){ 35 36 global $data; 37 38 $written = fwrite($socket, $data,1); 39 if ($written === strlen($data)) { 40 fclose($socket); 41 removeStream($socket); 42 } else { 43 $data = substr($data, $written); 44 } 45 46 } 47 48 //停止指定事件并销魂指定流资源 49 function removeStream($stream){ 50 51 global $arr,$loop; 52 $key = (int)$stream; 53 $pid = posix_getpid(); 54 55 if (!isset($arr[$pid][$key])) 56 return; 57 58 $arr[$pid][$key]->stop(); 59 unset($arr[$pid][$key]); 60 } 61 62 function acceptConnection($socket){ 63 64 $new_socket = @stream_socket_accept($socket); 65 if (!$new_socket) { 66 return; 67 } 68 69 stream_set_blocking($new_socket,false); 70 stream_set_read_buffer($new_socket, 0); 71 72 //add($new_socket,Ev::READ,'baseRead'); 73 add($new_socket,Ev::WRITE,'baseWrite'); 74 75 } 76 77 //定时器 78 function timers() 79 { 80 echo date("Y-m-d H:i:s") . "\n"; 81 } 82 83 $socketmain = stream_socket_server('tcp://127.0.0.1:7777'); 84 //非阻塞 85 stream_set_blocking($socketmain,false); 86 87 add($socketmain,Ev::READ,'acceptConnection'); 88 89 $event = $loop->timer(2,2,'timers'); 90 91 $loop->run(); 92 93 /* 94 while (true){ 95 //EV::RUN_ONCE 事件只触发1次 96 $loop->run(EV::RUN_ONCE); 97 } 98 */
利用telnet测试,具体参考WorkerMan源码分析 - 实现最简单的原型文章。