当前位置:首页 > WebSocket > 正文内容

workerman拆分events文件,实现常规编程习惯

陈杰2个月前 (12-22)WebSocket247

wokerman的gateway很好用,自动解决了扩展性,分布式等较底层的东西。

所有的逻辑代码只需要专注于events.php文件就行了,但是作者对于events编程的介绍比较少,如果从业多年的老手自然知道怎么去搭建自己喜欢的框架。

新入门朋友还需要摸索一下。

下面我们来拆分一下events.php,实现mvc框架编程的丝滑体验。。


首先看看目录结构

image.png


这里我们引入了一个thinkphp的orm

composer require "topthink/think-orm"

有轮子当然用啊,没有我们再造,还有一个东西,redis缓存就需要我们自己造了


我把redis放在App\Rdb.php下面

<?php
/**
 * Author:陈杰
 * Blog:http://blog.95shouyou.com
 * Email:823380606@qq.com
 * Git:https://gitee.com/chen95
 * Date:2020/12/22 0022
 * Time:10:07
 */
namespace App\Library;
class Rdb
{

    private $redis;
    private static $_instance = null; //定义单例模式的变量

    public static function getInstance()
    {
        if (empty(self::$_instance)) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    private function __construct()
    {
        $this->redis = new \Redis();
        $redis       = $this->redis->connect('127.0.0.1', 8379);
        if ($redis === false) {
            throw new \Exception('redis connect error');
        }
        $this->redis->auth("********");
        $this->redis->select(2);
    }

    public function get($key, $default = false)
    {
        $data = $this->redis->get($key);
        return $data ?? $default;
    }

    public function set($key, $data, $ttl)
    {
        $this->redis->
        $this->redis->set($key, $data, $ttl);
    }

    public function del($key)
    {
        return $this->redis->del($key);
    }

    public function query($command, $parameters)
    {
        return $this->redis->$command(...$parameters);
    }

    public static function __callStatic($method, $parameters)
    {
        return (new static())->{$method}(...$parameters);
    }
    
    /**
     * 防止clone多个实例
     */
    private function __clone()
    {
    }

    /**
     * 防止反序列化
     */
    private function __wakeup()
    {
    }
}


把redis做成单例,因为这个workerman是常驻内存的,所以用一个单例来维护redis连接。

当然这里我们用得比较低级,如果出现连接断开的问题怎么解决还没实现,感兴趣的可以自行百度连接池。


这样我们的作为curd男孩的两大必备的工具就用了,orm,redis。

接下来就是拆分了


看看events.php

<?php
/**
 * 用于检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
 * 然后观察一段时间workerman.log看是否有process_timeout异常
 */
//declare(ticks=1);
use App\Service\MemberService;
use \GatewayWorker\Lib\Gateway;
use think\facade\Db;
class Events
{
    use \App\Library\Tools;
    public static function onWorkerStart()
    {
        Db::setConfig([
            // 默认数据连接标识
            'default'     => 'mysql',
            // 数据库连接信息
            'connections' => [
                'mysql' => [
                    // 数据库类型
                    'type'     => 'mysql',
                    // 主机地址
                    'hostname' => '127.0.0.1',
                    // 用户名
                    'username' => 'root',
                    //密码
                    'password' => '*********',
                    // 数据库名
                    'database' => 'table',
                    // 数据库编码默认采用utf8
                    'charset'  => 'utf8',
                    // 数据库调试模式
                    'debug'    => false,
                ],
            ],
        ]);
    }

    public static function onConnect($client_id)
    {

    }

    /**
     * type值
     * 1    ping 心跳  返回1
     * 2    bind 绑定  返回2
     * 101    interview_member_join_room  集合室-用户进入集合室
     * 102    interview_member_down       集合室-用户坐下座位
     * 103    interview_member_up         集合室-用户离开座位
     * 400  错误       返回400
     */
    public static function onMessage($client_id, $message)
    {

        try {
            $message = json_decode($message, true);
            if (!is_array($message))
                return Gateway::sendToCurrentClient(self::error(400, '参数类型错误'));

            if ($message['type'] > 2 and !isset($_SESSION['member_id'])) {
                return Gateway::closeClient(self::error(400, '未登录'));
            }

            switch ($message['type']) {

                case 101:
                    // doSomeThing
                    break;
                case 2:
                    MemberService::bind($client_id, $message);
                    break;
                default :
                    return Gateway::sendToCurrentClient(self::success(1));
                    break;
            }

        } catch (\Exception $exception) {
            return Gateway::sendToCurrentClient(self::error(400, '参数类型错误'));
        }
    }

    public static function onClose($client_id)
    {

    }
}


现在我们的events.php就类似于我们的laravel thinkphp中的index.php了,就是一个入口文件,并且是带路由的。

当然这里有更好的实现,比如你可以添加一些前置中间件这些都行。

我这里就做了一个简单的实现,就是把所有的操作全部定义为type,对照我们的文档自行就行路由方法。


接下来看看我们的MemberService吧


<?php
/**
 * Author:陈杰
 * Blog:http://blog.95shouyou.com
 * Email:823380606@qq.com
 * Git:https://gitee.com/chen95
 * Date:2020/12/21 0021
 * Time:16:17
 */

namespace App\Service;

use GatewayWorker\Lib\Gateway;
use think\facade\Db;

class MemberService extends BaseService
{
    public static function bind($client_id, $message)
    {
        //绑定用户
        $token = $message['data']['token'];

        $authorizationInfo =
            explode(":", openssl_decrypt(base64_decode($token), 'DES-EDE3'
            , self::$key, OPENSSL_RAW_DATA));

        if (!is_array($authorizationInfo) || count($authorizationInfo) < 2 
        || $authorizationInfo[0] < 1)
            return Gateway::sendToCurrentClient(self::error(2, '登录参数错误'));

        $user   = Db::table('member_token')->where('member_id', $authorizationInfo[0])
        ->field('member_id,token')->find();
        $member = Db::table('member')->find($user['member_id']);

        if (!isset($user))
            return Gateway::sendToCurrentClient(self::error(2, '没有找到该用户'));

        if ($user['token'] !== $authorizationInfo[1])
            return Gateway::sendToCurrentClient(self::error(2, '登录验证错误'));

        Gateway::bindUid($client_id, $authorizationInfo[0]);

        $_SESSION['member']    = $member;
        $_SESSION['token']     = $user;
        $_SESSION['member_id'] = $member['id'];

        return Gateway::sendToCurrentClient(self::success(2));
    }
}


我们还在Library中用到了一个Tools的trait

看看代码

<?php
/**
 * Author:陈杰
 * Blog:http://blog.95shouyou.com
 * Email:823380606@qq.com
 * Git:https://gitee.com/chen95
 * Date:2020/12/21 0021
 * Time:16:49
 */

namespace App\Library;

trait Tools
{
    public static $key = "*********************";

    public static function success(int $type = 1, $data = [], $msg = '成功', $code = 200)
    {
        $error = [
            'type' => $type,
            'msg'  => $msg,
            'code' => $code,
            'data' => $data
        ];
        return json_encode($error, 256);
    }

    //类型错误
    public static function error(int $type = 400, $msg = '错误', $code = 400, $data = [])
    {
        $error = [
            'type' => $type,
            'msg'  => $msg,
            'code' => $code,
            'data' => $data
        ];
        return json_encode($error, 256);
    }
}


注意一下命名空间,这样实际上已经实现了我们的Events.php的拆分了,变成了我们比较常见的MVC编程的习惯。

把Events变成入口文件,不同的路由分别路由到不同的文件中进行业务处理。


当然还可以做得更完善,比如连接池,比如异常处理,比如中间件,比如验证器,等等等等。


最后还有最重要的一步要做,利用composer的自动加载把我们的写的代码加载进去

{
  "name": "workerman/gateway-worker-demo",
  "keywords": [
    "distributed",
    "communication"
  ],
  "homepage": "http://www.workerman.net",
  "license": "MIT",
  "require": {
    "workerman/gateway-worker": ">=3.0.0",
    "topthink/think-orm": "^2.0"
  },
  "autoload": {
    "psr-4": {
      "App\\": "App/"
    },
    "classmap": [
      "App/Library",
      "App/Service"
    ]
  }
}

主要是autoload下面的这几行,加载我们刚刚写的App目录下的文件,不加载的话会提示找不到类哦。



扫描二维码至手机访问

扫描二维码推送至手机访问。

版权声明:本文由何烦过虎溪发布,如需转载请注明出处。

转载请注明出处:http://95shouyou.com/?id=23

分享给朋友:

相关文章

申研社websocket通信规定

一、建立连接和维持连接 1、建立连接 测试地址ws://api.shenyanshe.95shouyou.com/websocket 2、约定客户端发送数据格式为 json字符串,...

Workerman-写一个简单的websocket双向通信并实现MVC,用来开发游戏服务器

最近在看Workerman,发现这是一个真的很好用的东西。文档相当丰富,而且大神walker很贴心,有问必答。下面记一下一个简单的基于GatewayWorker框架写的websocket的程序。传统的...

nginx下反向代理开启websocket的wss服务

nginx下反向代理开启websocket的wss服务

通过nginx配置好网站的ssl证书后添加   location /wss  {    proxy_pass http://127.0.0.1:828...

laravel利用websocket搭建一个简单的实时聊天系统

laravel利用websocket搭建一个简单的实时聊天系统

主要实现功能就是用户之间的的实时聊天。理一下业务逻辑:用户A给用户B发送消息>如果用户B在线,直接通过websocket推送消息,用户B接受到消息后写入前端数据库,并归档数据>如果用户B不...

uniapp的websocket无法监听到onSocketClose和onSocketError

项目框架使用的前端uniapp+后端laravel+workerman websocket写的,主要功能是聊天室系统。但是在开发完成实际体验过程中发现了一个有趣的现象。websocket老是无缘无故的...

workerman中捕获notice错误并抛出异常

在workerman中出现了notice错误并不会中断程序运行,这是抛出一个无关紧要的提示,这显然与我们的程序设计初衷不符。例如我们去获取数组中的一个key-value值,当key不存在的时候只是提示...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。