SWOOLE 从入门到放弃之写个小框架(十八)

释放双眼,带上耳机,听听看~!

小伙伴们,铁钩钓鱼了。。

为什么要搞个钩子呢?我来说一个场景吧,稍大点的项目IM和通知服务是不会放在一起的,应用场景不同,放在一起可能会造成资源浪费。所以大部分项目的IM和通知服务是分别放在不同的服务器上部署的。WEBSOCKET间的通信有可能是会被隔离的。怎么办呢,于是就有这么一个简单的处理办法。那就是,把各个WEBSOCKET的FD存到NOSQL数据库里,比如REDIS。然后根据不同的用户来获取它所在的服,然后就能实现跨服务通信了。

首先,我要先创建文件/app/common/Redis.php,这个类很简单,首先它是单例模式,REDIS的操作方法,全部用魔术方法来实现调用,代码如下

<?php
namespace app/common ;
class Redis
{
    private static $instance;
    private function __construct ()
    {
        try{
            self::$instance = new /Redis();
            self::$instance->pconnect (config('redis.host'),config ('redis.port'));
            if(config ('redis.passwd','')!=''){
                self::$_instance->auth(config ('redis.passwd'));
            }
            self::$instance->select(config ('redis.db'));
            /Piz/Log::get_instance()->write ("INFO","REDIS","已连接",config('redis.host').":".config ('redis.port'));
        }catch (/RedisException $e){
            self::$instance = NULL ;
            /Piz/Log::get_instance()->write ("INFO","REDIS",$e->getMessage ());
        }
    }

    public static function get_instance(){
        if(is_null (self::$instance)){
            new self();
        }
        return self::$instance;
    }

    public function __call($method ,$args=NULL){
        $this->handle->$method(...$args);
    }
}

缺一个redis的配置文件,创建它 config/redis.php ,配置很简单

<?php
return [
    'host'      => '127.0.0.1',
    'port'      => 6379,
    'passwd'    =>  '',
    'db'        => 0,
];

我们这里只是简单的实现一个REDIS的连接和操作,至于REDIS集群、HASH一致性等算法那就不是本篇考虑的内容了,小伙伴们可以自行百度。。

REDIS类的开发工作就完成了。接下来就同本篇的重点 钩子。

创建文件frame/Lib/Hook.php,老样子,单例模式,再来一个listen方法,有一个前提,有的时候在某一个位置可能需要共存多个钩子。怎么办呢?咱可以把它放到配置文件里。
完成Hook.php的代码前,先创建钩子的配置文件,在这个本置文件里,我只创建了三个点位,并且提前把要配置的信息也填了进去,代码如下

<?php
return [
    //Server::onStart
    'start'     => [
        [/app/hook/FD::class,'start'],
    ],
    //Server::onOpen
    'open'      => [
        [/app/hook/FD::class,'open'],
    ],
    //Server::onClose
    'close'     => [
        [/app/hook/FD::class,'close'],
    ],
];

小伙伴们能看明白吧。说白了就是我目前只需要在Server.php里的对应这三个回调方法里埋点。

接下来就写Hook.php。。代码如下

<?php
namespace Piz;

class Hook
{
    private static $instance;
    private static $config ;

    private function __construct ()
    {
    }

    public static function get_instance(){
        if(is_null (self::$instance)){
            self::$instance = new self();
            self::$config =  Config::get_instance ()->get("hook");
        }
        return self::$instance;
    }

    public function listen($hook , ...$args){
        $hooks = isset(self::$config[$hook]) ? self::$config[$hook] : [] ;
        while($hooks){
            list($class,$func) = array_shift ($hooks);
            try{
                $class::get_instance()->$func(...$args);
            }catch (/Exception $e){
                Log::get_instance ()->write ('ERROR',$e->getMessage ());
            }
        }
    }
}

代码是同样的简单。这次我在catch下埋了记录异常错误的日志点。这个写完了。我们要把它放到Server.php对应的点位了。修改代码如下

public function onStart($server){
        Log::get_instance()->write('INFO',"启动成功","{$this->config['ip']}:{$this->config['port']}");
        Hook::get_instance ()->listen('start',$server);
    }
public function onOpen( $server,$request){
        Log::get_instance()->write('DEBUG',"FD:{$request->fd}","握手成功");
        Hook::get_instance ()->listen('open',$server,$request->fd);
    }
public function onClose($server,$fd){
        Log::get_instance()->write('DEBUG',"FD:{$fd}","关闭连接");
        Hook::get_instance ()->listen('close',$server,$fd);
    }

Hook.php写完了,点也埋好了。接下来就是写对应的钩子业务逻辑了

首先,为了满足本篇开头说的记录连接的FD,必须有一个握手成功时要存储的方法,然后在用户断开后必须有一个销毁FD的方法,最重要的一个情况是,如果服务端断点,已存在的FD是没法继续保持的,怎么办呢?在启动时,把它们全部清掉。

接下来,创建文件app/hook/FD.php ,必须是单例械式,代码如下

<?php
/**
 * 管理FD
 */
namespace app/hook ;
class FD
{
    private static $instance;

    private function __construct ()
    {
    }

    public static function get_instance(){
        if(is_null (self::$instance)){
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function start($server){
        /app/common/Redis::get_instance ()->del("FD");
        /Piz/Log::get_instance()->write ("INFO","Hook","重置FD表");
    }

    public function open($server,$fd){
        /app/common/Redis::get_instance ()->sAdd("FD",$fd);
        /Piz/Log::get_instance()->write ("INFO","Hook","写入REDIS集合","FD:{$fd}");
    }

    public function close($server,$fd){
        /app/common/Redis::get_instance ()->sRem("FD",$fd);
        /Piz/Log::get_instance()->write ("INFO","Hook","移出REDIS集合","FD:{$fd}");
    }

    public function __call($method ,$args=NULL){
        $this->$method(...$args);
    }
}

启动start.php 看效果
SWOOLE 从入门到放弃之写个小框架(十八)

好玩了。。。

今天就到这了,周末如果有时间的话,就把Model简单说一下,其实Model很简单,小伙伴们可以自己整合一下你再习惯使用ORM或其它ORM。我也只是整合了一个别人的简单ORM。所以,这个可有可有。。不重要。
下周的主要工作就优化一下性通,优化一下启动程序,埋点监控之类的,基础上也就这些了。
以上工作完成后,我们就用这个小框架做几个小项目,让大家能更深入的理解它的应用方法。

【转自慕课】https://www.imooc.com

PHP

Laravel开发简书es一章问题:access_control_exception

2022-3-3 3:19:44

PHP

PHP创建和使用session cookie变量

2022-3-3 3:21:52

搜索