easyswoole 简单的redis 消息队列

一、配置 dev.php

 

 /*################ REDIS CONFIG ##################*/
    'REDIS' => [
        'host'          => '127.0.0.1',//ip地址
        'port'          => '6379',//端口
        'auth'          => '123456',//密码
        'POOL_MAX_NUM'  => '2',
        'POOL_MIN_NUM'  => '1',
        'POOL_TIME_OUT' => '0.1',
    ],

二、配置 EasySwooleEvent.php

 本次使用了 Process与 redis 相结合,一定要有这两个配置

<?php
namespace EasySwoole\EasySwoole;

use EasySwoole\EasySwoole\Crontab\Crontab;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\Http\Request;
use EasySwoole\Http\Response;


use EasySwoole\Socket\Dispatcher;
use App\WebSocket\WebSocketParser;
use App\WebSocket\WebSocketEvent;

use EasySwoole\ORM\Db\Connection;
use EasySwoole\ORM\DbManager;
use Swoole\Coroutine\Scheduler;
use EasySwoole\Mysqli\QueryBuilder;

use App\Process\TestProcess;
 

class EasySwooleEvent implements Event
{

    public static function initialize()
    {
        date_default_timezone_set('Asia/Shanghai');
		$config = new \EasySwoole\ORM\Db\Config(Config::getInstance()->getConf('MYSQL'));
        $config->setMaxObjectNum(20);//配置连接池最大数量
        DbManager::getInstance()->addConnection(new Connection($config));
        //创建一个协程调度器
        $scheduler = new Scheduler();
        $scheduler->add(function () {
            $builder = new QueryBuilder();
            $builder->raw('select version()');
            DbManager::getInstance()->query($builder, true);
            //这边重置ORM连接池的pool,避免链接被克隆岛子进程,造成链接跨进程公用。
            //DbManager如果有注册多库链接,请记得一并getConnection($name)获取全部的pool去执行reset
            //其他的连接池请获取到对应的pool,然后执行reset()方法
            DbManager::getInstance()->getConnection()->getClientPool()->reset();
        });
        //执行调度器内注册的全部回调
        $scheduler->start();
        //清理调度器内可能注册的定时器,不要影响到swoole server 的event loop
        \Swoole\Timer::clearAll();

    }

    public static function mainServerCreate(EventRegister $register)
    {
        $register->add($register::onWorkerStart,function (){
            //链接预热
            DbManager::getInstance()->getConnection()->getClientPool()->keepMin();
        });
		/**
		 * **************** websocket控制器 **********************
		 */
		// 创建一个 Dispatcher 配置
		$conf = new \EasySwoole\Socket\Config();
		// 设置 Dispatcher 为 WebSocket 模式
		$conf->setType(\EasySwoole\Socket\Config::WEB_SOCKET);
		// 设置解析器对象
		$conf->setParser(new WebSocketParser());
		// 创建 Dispatcher 对象 并注入 config 对象
		$dispatch = new Dispatcher($conf);
		// 给server 注册相关事件 在 WebSocket 模式下  on message 事件必须注册 并且交给 Dispatcher 对象处理
		$register->set(EventRegister::onMessage, function (\swoole_websocket_server $server, \swoole_websocket_frame $frame) use ($dispatch) {
			$dispatch->dispatch($server, $frame->data, $frame);
		});
        // 注册服务事件
        $register->add(EventRegister::onOpen, [WebSocketEvent::class, 'onOpen']);
        $register->add(EventRegister::onClose, [WebSocketEvent::class, 'onClose']);
		/**
         * **************** redis **********************
         */
        $config = new \EasySwoole\Pool\Config();
        $redisConfig = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS'));
        \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig),'redis');
        /**
         * ****************  Process 设置 **********************
         */
        $processConfig = new \EasySwoole\Component\Process\Config();
        $processConfig->setProcessName('testProcess');//设置进程名称
        \EasySwoole\Component\Process\Manager::getInstance()->addProcess(new TestProcess($processConfig));
         

    }

    public static function onRequest(Request $request, Response $response): bool
    {
        // TODO: Implement onRequest() method.
        return true;
    }

    public static function afterRequest(Request $request, Response $response): void
    {
        // TODO: Implement afterAction() method.
    }
}

三、新建 \App\Process\TestProcess.php 文件

<?php
namespace App\Process;
use EasySwoole\Component\Process\AbstractProcess;
use Swoole\Process;
use EasySwoole\EasySwoole\ServerManager;
use EasySwoole\Pool\Manager;
use EasySwoole\EasySwoole\Task\TaskManager;
use App\Models\User;
use EasySwoole\EasySwoole\Logger;

class TestProcess extends AbstractProcess {
	
	
    protected function run($arg)
    {
        //当进程启动后,会执行的回调
        // 每隔 10 秒执行一次
         //Logger::getInstance()->info('log level info');//记录info级别日志并输出到控制台
        \EasySwoole\Component\Timer::getInstance()->loop(10 * 1000, function ()  {
            $redis = Manager::getInstance()->get('redis')->getObj();
            $server = ServerManager::getInstance()->getSwooleServer();
            $res = $redis->exists('user_list');
            if(!empty($res)){
                $uid = $redis->lPop('user_list');
                $fd = $redis->get('uid_'.$uid);
                if($fd){
                    //推送通知到用户
                    $server->push($fd,'push in http at '. date('H:i:s'));
                }
                //添加异步操作 修改数据库数据状态
                $task = TaskManager::getInstance();
                $task->async(function () use($fd){
                    $user = User::create()->get($fd);
                    $user->update([
                        'status' => 2
                    ]);
                    echo "异步调用task1\n";
                });
            }
            //回收redis
            Manager::getInstance()->get('redis')->recycleObj($redis);
        });
        echo 'Test Process start'."\n";
    }

    protected function onPipeReadable(Process $process)
    {
        /*
         * 该回调可选
         * 当有主进程对子进程发送消息的时候,会触发的回调,触发后,务必使用
         * $process->read()来读取消息
         */
    }

    protected function onShutDown()
    {
        echo 'Test Process end'."\n";
        /*
         * 该回调可选
         * 当该进程退出的时候,会执行该回调
         */
    }

    protected function onException(\Throwable $throwable, ...$args)
    {
        /*
         * 该回调可选
         * 当该进程出现异常的时候,会执行该回调
         */
    }
	
}

四、配置一下开始与结束的事件 本次使用的是 WebSocket 所以配置App\WebSocket\WebSocketEvent.php

<?php
namespace App\WebSocket;
use App\Models\User;
use EasySwoole\EasySwoole\Task\TaskManager;
use EasySwoole\Pool\Manager;
use App\Event\Event;
/**
 * Class WebSocketEvent
 *
 * 此类是 WebSocket 中一些非强制的自定义事件处理
 *
 * @package App\WebSocket
 */
class WebSocketEvent
{
    /**
     * 握手事件
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    public function onHandShake(\swoole_http_request $request, \swoole_http_response $response)
    {
        /** 此处自定义握手规则 返回 false 时中止握手 */
        if (!$this->customHandShake($request, $response)) {
            $response->end();
            return false;
        }

        /** 此处是  RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
        if ($this->secWebsocketAccept($request, $response)) {
            $response->end();
            return true;
        }

        $response->end();
        return false;
    }

    /**
     * 自定义握手事件
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    protected function customHandShake(\swoole_http_request $request, \swoole_http_response $response): bool
    {
        /**
         * 这里可以通过 http request 获取到相应的数据
         * 进行自定义验证后即可
         * (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
         */
        $headers = $request->header;
        $cookie = $request->cookie;

        // if (如果不满足我某些自定义的需求条件,返回false,握手失败) {
        //    return false;
        // }
        return true;
    }

    /**
     * RFC规范中的WebSocket握手验证过程
     * 以下内容必须强制使用
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    protected function secWebsocketAccept(\swoole_http_request $request, \swoole_http_response $response): bool
    {
        // ws rfc 规范中约定的验证过程
        if (!isset($request->header['sec-websocket-key'])) {
            // 需要 Sec-WebSocket-Key 如果没有拒绝握手
            var_dump('shake fai1 3');
            return false;
        }
        if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
            || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
        ) {
            //不接受握手
            var_dump('shake fai1 4');
            return false;
        }

        $key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        $headers = array(
            'Upgrade'               => 'websocket',
            'Connection'            => 'Upgrade',
            'Sec-WebSocket-Accept'  => $key,
            'Sec-WebSocket-Version' => '13',
            'KeepAlive'             => 'off',
        );

        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        // 发送验证后的header
        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        // 接受握手 还需要101状态码以切换状态
        $response->status(101);
        var_dump('shake success at fd :' . $request->fd);
        return true;
    }

    /**
     * 关闭事件
     *
     * @param \swoole_server $server
     * @param int            $fd
     * @param int            $reactorId
     */
    static function onClose(\swoole_server $server, int $fd, int $reactorId)
    {

        $info = $server->getClientInfo($fd);
        /**
         * 判断此fd 是否是一个有效的 websocket 连接
         * 参见 https://wiki.swoole.com/wiki/page/490.html
         */
        if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
            /**
             * 判断连接是否是 server 主动关闭
             * 参见 https://wiki.swoole.com/wiki/page/p-event/onClose.html
             */
            if ($reactorId < 0) {
                echo "server close \n";
            }else{
                //清除绑定uid 与 fd
                $redis = Manager::getInstance()->get('redis')->getObj();
                $uid = $redis->get('fd_'.$fd);
                $redis->del('fd_'.$fd);
                $redis->del('uid_'.$uid);
                echo "server close at fd: ".$fd."\n";
            }
        }
    }

    /**
     * 打开了一个链接
     * @param swoole_websocket_server $server
     * @param swoole_http_request $request
     */
    static function onOpen(\swoole_websocket_server $server, \swoole_http_request $request)
    {
		Event::getInstance()->hook('test');
		
        $token = $request->get['token'];
        $fd = $request->fd;
        if(!isset($token)){
            $data = [
                "type" => "token expire"
            ];
            $server->push($request->fd, json_encode($data));
            $server->close($fd);
            return;
        }
        $user_info = User::create()->where('token', $token)->get();
        if(empty($user_info)){
            $data = [
                "type" => "token expire"
            ];
            $server->push($fd, json_encode($data));
            $server->close($fd);
            return;
        }
        //绑定uid 与 fd
        $redis = Manager::getInstance()->get('redis')->getObj();
        if($redis->exists('uid_'.$user_info['id'])){
            $old_fd = $redis->getSet('uid_'.$user_info['id'],$fd);
            $redis->del('fd_'.$old_fd);
            $redis->set('fd_'.$fd,$user_info['id']);
        }else{
            $redis->set('uid_'.$user_info['id'],$fd);
            $redis->set('fd_'.$fd,$user_info['id']);
        }
        echo "server connection at fd: ".$request->fd."\n";
        $server->push($fd, json_encode($user_info));
    }

}

 五、本次使用了一下  model  目录App\Models\User.php 需要手动配置一下哦!

<?php

namespace App\Models;

use EasySwoole\ORM\AbstractModel;

/**
 * 用户模型
 * Class User
 */
class User extends AbstractModel
{
     /**
      * @var string 
      */
     protected $tableName = 'fa_user';
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值