直播房间竞拍实现-PHP7.3+swool扩展+redis缓存+Apicloud业务实现

基础环境要求->PHP7+redis+mysql5.6+swool
客户端支持->APIcloud环境+vuejs库+websocket支持

<?php
/**
 * Created by PhpStorm.
 * User: 835173372@qq.com
 * NickName: 老孟编程
 * Date: 2019/8/28 0028 14:55
 */

error_reporting(0);

use think\cache\Redis;

if (!preg_match("/cli/i", php_sapi_name())) {
    //echo '此脚本运行在命令号环境下', PHP_EOL;
    //exit;
}


class DB
{
    #top51.cn
    public    $pdo;
    protected $res;
    protected $config;

    function __construct($config)
    {
        $this->Config = $config;
        $this->connect();
    }

    function __destruct()
    {
        $this->pdo = null;
    }

    public function connect()
    {
        $this->pdo = new PDO($this->Config['dsn'], $this->Config['name'], $this->Config['password']);
        $this->pdo->query('set names utf8;');
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    public function close()
    {
        $this->pdo = null;
    }

    public function query($sql)
    {
        $res = $this->pdo->query($sql);
        if ($res) {
            $this->res = $res;
        }
    }

    public function exec($sql)
    {
        $res = $this->pdo->exec($sql);
        if ($res) {
            $this->res = $res;
        }
    }

    public function getAll($sql)
    {
        $this->query($sql);
        return $this->res->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getRow($sql)
    {
        $this->query($sql);
        return $this->res->fetch(PDO::FETCH_ASSOC);
    }

    public function getValue($sql)
    {
        $this->query($sql);
        return $this->res->fetchColumn(PDO::FETCH_ASSOC);
    }

    public function getOne($sql)
    {
        $this->query($sql);
        return $this->res->fetchColumn(PDO::FETCH_ASSOC);
    }

    public function insertId()
    {
        return $this->pdo->lastInsertId();
    }

    public function add($table, $dataset, $debug = 0)
    {
        if (empty($table)) {
            throw new Exception("表名不能为空.");
        }
        if (!is_array($dataset) || count($dataset) <= 0) {
            throw new Exception('没有要插入的数据');
        }
        $value = '';
        while (list($key, $val) = each($dataset))
            $value .= "`{$key}`='{$val}',";
        $value = substr($value, 0, -1);

        if ($debug === 0) {
            $this->query("insert into `{$table}` set {$value}");
            if (!$this->res) {
                return FALSE;
            } else {
                return $this->insertId();
            }
        } else {
            echo "insert into `{$table}` set {$value}";
            if ($debug === 2) {
                exit;
            }
        }
    }

    public function update($table, $dataset, $conditions = "", $debug = 0)
    {
        if (empty($table)) {
            throw new Exception("表名不能为空.");
        }
        if (!is_array($dataset) || count($dataset) <= 0) {
            throw new Exception('没有要更新的数据');
            return false;
        }
        if (empty($conditions)) {
            throw new Exception("删除条件为空哦.");
        }
        $conditions = " where " . $conditions;
        $value      = '';
        while (list($key, $val) = each($dataset))
            $value .= "`$key`='$val',";
        $value = substr($value, 0, -1);

        //数据库操作
        if ($debug === 0) {
            $this->exec("update `{$table}` set {$value} {$conditions}");
            return $this->res;
        } else {
            echo "update `{$table}` set {$value} {$conditions}";
            if ($debug === 2) {
                exit;
            }
        }
    }

    public function delete($table, $conditions = "", $debug = 0)
    {
        if (empty($table)) {
            throw new Exception("表名不能为空.");
        }
        if (empty($conditions)) {
            throw new Exception("删除条件为空哦.");
        }
        $conditions = " where " . $conditions;
        //数据库操作
        if ($debug === 0) {
            $this->exec("delete from {$table} {$conditions}");
            return $this->res;
        } else {
            echo "delete from {$table} {$conditions}";
            if ($debug === 2) {
                exit;
            }
        }
    }
}

class Cache
{
    /*
    * $redis = new Cache();
    * $list = $redis->get('House102');
    */
    protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16];
    protected        $options
                                = [
            'type'       => 'redis',
            'host'       => '127.0.0.1',
            'port'       => 6379,
            'password '  => '',
            'select'     => 0,
            'timeout'    => 0,
            'expire'     => 0,
            'persistent' => false,
            'prefix'     => '',
            'serialize'  => true,
        ];

    protected $handler = null;//redis句柄

    protected $tag = null;//tag

    public function __construct($options = [])
    {
        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }

        if (extension_loaded('redis')) {
            $this->handler = new \Redis();
            $this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
            if ('' != $this->options['password']) {
                $this->handler->auth($this->options['password']);
            }
            if ('' != $this->options['select']) {
                $this->handler->select($this->options['select']);
            }
        } else {
            throw new \Exception('extension :redis is not install');
        }
    }

    public function has($name)
    {
        return $this->handler->exists($this->getCacheKey($name));
    }

    public function get($name, $default = false)
    {

        $value = $this->handler->get($this->getCacheKey($name));
        if (is_null($value) || false === $value) {
            return $default;
        }
        return $this->unserialize($value);
    }

    public function rm($name)
    {
        return $this->handler->delete($this->getCacheKey($name));
    }

    public function set($name, $value, $expire = null)
    {
        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }

        $key = $this->getCacheKey($name);

        $value = $this->serialize($value);

        if ($this->tag && !$this->has($name)) {
            $this->setTagItem($key);//tag和此缓存绑定起来
        }

        if ($expire) {
            $result = $this->handler->setex($key, $expire, $value);
        } else {
            $result = $this->handler->set($key, $value);
        }
        return $result;
    }

    public function clear($tag = null)
    {
        if ($tag) {
            $keys = $this->getTagItem($tag);
            $this->handler->del($keys);

            $tagName = $this->getTagKey($tag);
            $this->handler->del($tagName);
            return true;
        }
        return $this->handler->flushDB();
    }

    public function tag($name, $keys = null, $overlay = false)
    {
        if (is_null($keys)) {
            $this->tag = $name;
        } else {
            $tagName = $this->getTagKey($name);
            if ($overlay) {
                $this->handler->del($tagName);
            }

            foreach ($keys as $key) {
                $this->handler->sAdd($tagName, $key);
            }
        }

        return $this;
    }

    protected function setTagItem($name)
    {
        if ($this->tag) {
            $tagName = $this->getTagKey($this->tag);
            $this->handler->sAdd($tagName, $name);
        }
    }

    protected function getTagItem($tag)
    {
        $tagName = $this->getTagKey($tag);
        return $this->handler->sMembers($tagName);
    }

    public function serialize($data)
    {
        if (is_scalar($data) || !$this->options['serialize']) {
            return $data;
        }
        $serialize = self::$serialize[0];
        return self::$serialize[2] . $serialize($data);
    }

    protected function unserialize($data)
    {
        if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) {
            $unserialize = self::$serialize[1];

            return $unserialize(substr($data, self::$serialize[3]));
        } else {
            return $data;
        }
    }

    protected function getCacheKey($name)
    {
        return $this->options['prefix'] . $name;
    }

    protected function getTagKey($tag)
    {
        return 'tag_' . md5($tag);
    }

    public static function getInstance($option = [])
    {
        $redis = new self($option);
        return $redis->handler;
    }
}

class wsserver
{

    public $db    = '';
    public $ws    = '';
    public $redis = '';

    public function __construct($config)
    {
        $this->db    = new DB($config);
        $this->redis = new Cache();
        if (preg_match("/cli/i", php_sapi_name())) {
            $this->ws = new Swoole\WebSocket\Server("0.0.0.0", 9501);
            $this->open();
            $this->message();
            $this->close();
            $this->ws->start();
        }
    }

    public function open()
    {
        $this->ws->on('open', function (Swoole\WebSocket\Server $server, $request) {
            echo "server: handshake success with fd{$request->fd}\n";
        });
    }

    public function message()
    {
        $this->ws->on('message', function (Swoole\WebSocket\Server $server, $frame) {

            echo '当前用户FD=' . $frame->fd, PHP_EOL;
            if ($frame->data == 'ping') {
                return false;//心跳处理,避免自动掉线
            }
            if (isset($frame->data)) {
                $pageparm = json_decode($frame->data, true);
                print_r($pageparm);


                #出价记录
                $house_jlist = $this->redis->get('House' . $pageparm['id']);
                if ($house_jlist && isset($house_jlist['list'])) {
                    $jlist = $house_jlist['list'];
                } else {
                    $jlist = [];
                }

                ###房间信息
                $paimai_house = $this->db->getRow("select * from oc_paimai_house where id='{$pageparm['id']}'");
                $paimai_house['start_time'] = $paimai_house['create_time'] + $paimai_house['autop_num'] * 60;
                $paimai_house['end_time']   = $paimai_house['create_time'] + ($paimai_house['autop_num'] + $paimai_house['time_down']) * 60;
                if (time() > $paimai_house['start_time'] && time() < $paimai_house['end_time'] && $paimai_house['status'] == 1) {
                    $paimai_house['doing'] = 1;
                }
                if (time() < $paimai_house['start_time'] && $paimai_house['status'] == 1) {
                    $paimai_house['doing'] = 2;
                }else{
                    $paimai_house['doing'] = 1;
                }

                ###房间信息
                switch ( $pageparm['act'] ) {

                    #离开房间
                    case 'leave':
                        $plist        = $this->db->getAll("select a.*,b.* from oc_paihouse_people as a left join  oc_user b on a.user_id=b.user_id where a.house_id = '{$pageparm['id']}'");

                        foreach ($plist as $k => $v) {
                            $fd = $v['fd'];
                            go(function () use ($fd, $plist, $paimai_house, $jlist, $server) {
                                $server->push($fd, json_encode([
                                    'fd'           => $fd,
                                    'peopleList'   => $plist,
                                    'paimai_house' => $paimai_house,
                                    'jlist'        => $jlist,
                                ], 320));
                            });
                        }
                        break;
                    #进入房间
                    case 'online':
                        $user            = $this->db->getRow("select * from oc_user where token = '{$pageparm['token']}'");
                        $paihouse_people = $this->db->getRow("select * from oc_paihouse_people where user_id = '{$user['user_id']}'");
                        if ($paihouse_people) {
                            $this->db->update('oc_paihouse_people', [
                                'house_id'    => $pageparm['id'],
                                'fd'          => $frame->fd,
                                'create_time' => time(),
                            ], "user_id={$user['user_id']}");
                        } else {
                            $this->db->add('oc_paihouse_people', [
                                'user_id'     => $user['user_id'],
                                'house_id'    => $pageparm['id'],
                                'fd'          => $frame->fd,
                                'create_time' => time(),
                            ]);
                        }
                        #客户端推送信息
                        $plist        = $this->db->getAll("select a.*,b.* from oc_paihouse_people as a left join  oc_user b on a.user_id=b.user_id where a.house_id = '{$pageparm['id']}'");
                        foreach ($plist as $k => $v) {
                            $fd = $v['fd'];
                            go(function () use ($fd, $plist, $paimai_house, $jlist, $server) {
                                $server->push($fd, json_encode([
                                    'fd'           => $fd,
                                    'peopleList'   => $plist,
                                    'paimai_house' => $paimai_house,
                                    'jlist'        => $jlist,
                                ], 320));
                            });
                        }
                        break;
                    #竞拍出价(其他操作)
                    default:
                        $plist        = $this->db->getAll("select a.*,b.* from oc_paihouse_people as a left join  oc_user b on a.user_id=b.user_id where a.house_id = '{$pageparm['id']}'");
                        foreach ($plist as $k => $v) {
                            $fd = $v['fd'];
                            go(function () use ($fd, $plist, $paimai_house, $jlist, $server) {
                                $server->push($fd, json_encode([
                                    'fd'           => $fd,
                                    'peopleList'   => $plist,
                                    'paimai_house' => $paimai_house,
                                    'jlist'        => $jlist,
                                ], 320));
                            });
                        }
                }
            }

        });
    }

    #异常退出处理
    public function close()
    {
        $this->ws->on('close', function (Swoole\WebSocket\Server $server, $fd) {

            echo "client {$fd} closed\n";
            $paihouse_people = $this->db->getRow("select * from oc_paihouse_people where fd='{$fd}'");

            $house_jlist = $this->redis->get('House' . $paihouse_people['house_id']);
            if ($house_jlist && isset($house_jlist['list'])) {
                $jlist = $house_jlist['list'];
            } else {
                $jlist = [];
            }
            $this->db->delete('oc_paihouse_people', "fd='{$fd}'");

            #客户端推送信息
            $plist        = $this->db->getAll("select a.*,b.* from oc_paihouse_people as a left join  oc_user b on a.user_id=b.user_id where a.house_id = '{$paihouse_people['house_id']}'");
            $paimai_house = $this->db->getRow("select * from oc_paimai_house where id='{$paihouse_people['house_id']}'");
            $paimai_house['start_time'] = $paimai_house['create_time'] + $paimai_house['autop_num'] * 60;
            $paimai_house['end_time']   = $paimai_house['create_time'] + ($paimai_house['autop_num'] + $paimai_house['time_down']) * 60;
            if (time() > $paimai_house['start_time'] && time() < $paimai_house['end_time'] && $paimai_house['status'] == 1) {
                $paimai_house['doing'] = 1;
            }
            if (time() < $paimai_house['start_time'] && $paimai_house['status'] == 1) {
                $paimai_house['doing'] = 2;
            }else{
                $paimai_house['doing'] = 1;
            }
            foreach ($plist as $k => $v) {
                $fdTemp = $v['fd'];
                go(function () use ($fdTemp, $plist, $paimai_house, $jlist, $server) {
                    $server->push($fdTemp, json_encode([
                        'fd'           => $fdTemp,
                        'peopleList'   => $plist,
                        'paimai_house' => $paimai_house,
                        'jlist'        => $jlist,
                    ], 320));
                });
            }
        });
    }

    public function testlist()
    {
        $sql  = "select * from oc_paimai_goods";
        $list = $this->db->getAll($sql);
        return $list;
    }
}

强调:代码在linux环境下直接执行,swool默认开启4个进程,给截图显示

后端代码实现
备注:
此业务环境是,多个房间进行竞拍活动,可以衍生到多房间直播聊天,如六间房之类的互动平台。
用Node.js+socket.io应该会更简单点,redis和mysql还是少不了,核心就是事件监听+事件中的业务处理,个推和群推。

群推利用了 swool的协程异步处理,有一定优势,还需要进一步观察稳定性。
后续会追加观察结果,尤其是并发观察。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值