• PHP之高性能I/O框架:Libevent(二)


    Event扩展

    Event可以认为是替代libevent最好的扩展,因为libevent已经很久不更新了,而Event一直在更新,而且Event支持更多特性,使用起来也比libevent简单。

    Event地址: http://pecl.php.net/package/event
    Event文档: http://docs.php.net/event

    和libevent一样,系统需要先安装 Libevent 库,因为都是基于 Libevent 库开发的:

    yum install libevent-dev
    

    然后安装PHP扩展。

    PHP7安装:

    pecl install event
    

    Event扩展不支持PHP5。

    注:后面的代码示例均使用的php7.1 + event环境。

    基本使用

    我们把libevent_tcp_server.php的例子改为Event实现的:

    event_tcp_server.php

    <?php 
    /**
     * Created by PhpStorm.
     * User: 公众号: 飞鸿影的博客(fhyblog)
     * Date: 2018/6/23
     */
     
    $receive = [];
    $master = [];
    $buffers = [];
    
    $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
    if (false === $socket ) {
        echo "$errstr($errno)
    ";
        exit();
    }
    if (!$socket) die($errstr."--".$errno);
    //stream_set_blocking($socket,0); //可选
    $id = (int)$socket;
    $master[$id] = $socket;
    
    echo "waiting client...
    ";
    
    
    //accept事件回调函数,参数分别是$fd, $events, $arg
    function ev_accept($socket, $flag, $base){
        global $receive;
        global $master;
        global $buffers;
    
        $connection = stream_socket_accept($socket);
        stream_set_blocking($connection, 0);//必须
        $id = (int)$connection;
    
        echo "new Client $id
    ";
    
        $event = new Event($base, $connection, Event::READ | Event::PERSIST, 'ev_read', $id); 
        $event->add();
    
        $master[$id] = $connection; //根据业务可选
        $receive[$id] = ''; //根据业务可选
        $buffers[$id] = $event; //根据业务可选
    }
    
    //read事件回调函数
    function ev_read($buffer, $flag, $id)
    {
        
        global $receive;
        global $master;
        global $buffers;
    
        //该方法里的$buffer和$master[$id]指向相同的内容
        // var_dump(func_get_args(), $master[$id] );
    
        //循环读取并解析客户端消息
        while( 1 ) {
            $read = @fread($buffer, 1024);
    
            //客户端异常断开
            if($read === '' || $read === false){
                break;
            }
    
            $pos = strpos($read, "
    ");
            if($pos === false)
            {
                $receive[$id] .= $read;
                // echo "received:".$read.";not all package,continue recdiveing
    ";
            }else{
                $receive[$id] .= trim(substr ($read,0,$pos+1));
                $read = substr($read,$pos+1);
                
                switch ( $receive[$id] ){
                    case "quit":
                        echo "client close conn
    ";
                        
                        //关闭客户端连接
                        unset($master[$id]);//断开客户端连接
                        unset($buffers[$id]);//删除事件
                        break;
                    default:
                        // echo "all package:
    ";
                        echo $receive[$id]."
    ";
                        break;
                }
                $receive[$id]='';
            }
        }
    }
    
    //创建全局event base
    $base = new EventBase();
    //创建并设置 event:其中$events设置为READ | PERSIST ;回调事件为ev_accept,参数 $base
    //PERSIST可以让注册的事件在执行完后不被删除,直到调用Event::del()删除.
    $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base); 
    $event->add();
    echo  "start run...
    ";
    
    //进入事件循环
    $base->loop();
    
    //下面这句不会被执行
    echo "This code will not be executed.
    ";
    
    

    可以发现做的改动非常小,而且代码更简洁了。运行脚本后,我们使用telnet测试,效果一模一样。

    使用Buffer

    直接看例子吧,还是基于上面的例子改的,注释里写得很清楚了:

    event_buffer_tcp_server.php

    <?php 
    /**
     * Created by PhpStorm.
     * User: 公众号: 飞鸿影的博客(fhyblog)
     * Date: 2018/6/23
     */
    
    $receive = [];
    $master = [];
    $buffers = [];
    
    $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
    if (false === $socket ) {
        echo "$errstr($errno)
    ";
        exit();
    }
    if (!$socket) die($errstr."--".$errno);
    //stream_set_blocking($socket,0);//可选
    $id = (int)$socket;
    $master[$id] = $socket;
    
    echo "waiting client...
    ";
    
    
    //accept事件回调函数,参数分别是$fd, $events, $arg
    function ev_accept($socket, $flag, $base){
        global $receive;
        global $master;
        global $buffers;
    
        $connection = stream_socket_accept($socket);
        //stream_set_blocking($connection, 0);//可选
        $id = (int)$connection;
    
        echo "new Client $id
    ";
    
        //新建EventBuffer 事件
        $event = new EventBufferEvent($base, $connection, 0, 'ev_read', 'ev_write', 'ev_status', $id); 
        $event->setTimeouts(30, 30); //read and write timeout 
        $event->setWatermark ( Event::READ, 0, 0xffffff ); //Adjusts read and/or write watermarks
        $event->setPriority(10);
        $event->enable(Event::READ | Event::PERSIST);
    
        $master[$id] = $connection; //如果去掉该行,客户端直接被断开
        $receive[$id] = ''; //如果去掉该行,服务端无法正常收到消息
        $buffers[$id] = $event; //如果去掉该行,客户端强制断开再连接,服务端无法正常收到消息
    }
    
    //read事件回调函数,参数分别是EventBufferEvent,arg 
    function ev_read($buffer, $id)
    {
        
        global $receive;
        global $master;
        global $buffers;
    
        //该方法里的$buffer和$buffers[$id]指向相同的内容
        // var_dump(func_get_args(), $buffers[$id], $master[$id]);
    
        //循环读取并解析客户端消息
        while( 1 ) {
            $read = $buffer->read(65535);
            // var_dump($read);
    
            //客户端异常断开;这里可能返回NULL
            if($read === '' || $read === false || $read === NULL){
                break;
            }
    
            $pos = strpos($read, "
    ");
            if($pos === false)
            {
                $receive[$id] .= $read;
                echo "received:".$read.";not all package,continue recdiveing
    ";
            }else{
                $receive[$id] .= trim(substr ($read,0,$pos+1));
                $read = substr($read,$pos+1);
                
                switch ( $receive[$id] ){
                    case "quit":
                        echo "client close conn
    ";
                        
                        //关闭客户端连接
                        unset($master[$id]);//断开客户端连接
                        unset($buffers[$id]);//删除事件
                        break;
                    default:
                        // echo "all package:
    ";
                        echo $receive[$id]."
    ";
                        break;
                }
                $receive[$id]='';
            }
        }
    }
    
    function ev_write($buffer, $id)
    {
        echo "$id -- " ."
    ";
    }
    
    function ev_status($buffer, $events, $id)
    {
        echo "ev_status - ".$events."
    ";
    }
    
    //创建全局event base
    $base = new EventBase();
    //创建并设置 event:其中$events设置为READ | PERSIST ;回调事件为ev_accept,参数 $base
    //PERSIST可以让注册的事件在执行完后不被删除,直到调用Event::del()删除.
    $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base); 
    $event->add();
    echo  "start run...
    ";
    
    //进入事件循环
    $base->loop();
    
    //下面这句不会被执行
    echo "This code will not be executed.
    ";
    

    定时器(Timer)

    直接看示例:
    event_timer.php

    <?php
    /**
     * Created by PhpStorm.
     * User: 公众号: 飞鸿影的博客(fhyblog)
     * Date: 2018/6/23
     */
     
    $base  = new  EventBase ();
    $n  =  2 ; //sec
    
    //初始化定时器
    $e  =  Event :: timer ( $base , function( $arg ) use (& $e ) {
        echo  " $arg  seconds elapsed
    " ;
         $e -> delTimer ();
    },  $n );
    
    //添加定时器
    $e -> addTimer ( $n ); //sec
    
    $base -> loop ();
    

    运行:

    $ php event_timer.php 
     2  seconds elapsed
    

    libevent扩展一样,Event::timer也是对Event的封装:

    <?php
    /**
     * Created by PhpStorm.
     * User: 公众号: 飞鸿影的博客(fhyblog)
     * Date: 2018/6/23
     */
     
    $base  = new  EventBase ();
    $n  =  2 ; //sec
    
    //初始化定时器
    $event = new Event($base, null, Event::TIMEOUT, 'ev_timer',  $n );
    $event->add($n);//sec
    
    function ev_timer($fd, $what, $arg){
        echo  " $arg  seconds elapsed
    " ;
        global $event;
        $event->del();
    }
    
    $base->loop();
    

    Event提供的定时器精度是秒。

    信号(Signal)

    Event 扩展提供了信号(Signal)操作的函数。

    <?php
    /**
     * Created by PhpStorm.
     * User: 公众号: 飞鸿影的博客(fhyblog)
     * Date: 2018/6/23
     */
     
    $base  = new  EventBase ();
    
    //初始化信号事件
    $e  =  Event :: signal ( $base , SIGUSR1, function( $signum , $arg ) use (& $e ) {
        echo  " Caught signal $signum
    " ;
        $e->delSignal(); //移除信号
    },  '');
    
    //安装信号
    $e -> addSignal (); //sec
    
    //发送信号
    posix_kill(posix_getpid (),  SIGUSR1);
    
    $base -> loop ();
    

    相比pcntl_signalEvent :: signal 高效很多。

    总结

    Libevent 非常强大,Event实现了其很多的接口供PHP调用,我这里仅是使用了常用的几个特性。由于Event能参考的资料实在是有限,这章写起来也相对难一些,例子里还是留了一些待再次理解。

    (未完待续)


    推荐

    Redis 系列讲座合集

    内容概要:Redis 最为目前炙手可热的 Key-Value 数据库,常用做缓存、Session共享中间件,分布式锁等等。
    本系列课程包括:

    讲师是CSDN 博客专家,多年 Redis 使用经验。感兴趣的朋友可以点击试看!


  • 相关阅读:
    选择结构
    算法和流程图
    存储信息和信息运算
    计算机组成原理
    计算机硬件组装
    认识计算机硬件
    认识操作系统
    DOS简介
    计算机软件知识
    易企cms截取字段方法
  • 原文地址:https://www.cnblogs.com/52fhy/p/9258040.html
Copyright © 2020-2023  润新知