Redis实践操作之—— 直播视频定时控制【TCP长连接框架(WorkerMan)+键空间通知的机制 ( Keyspace Notifications)+短信接口(API)】

一、思路梳理

  1. 同步直播视频到Redis
  2. 用户观看直播模板,点击直播按钮,检查是否有权限。
  3. 直播定时免费观看(免费观看10分钟),用户点击播放按钮开始,异步检查获取直播活动设置的免费观看时间(后台维护人员设置,Redis的hash存储信息),是否是直播。
  4. 是直播视频:判断该客户是否已经观看过了免费的20分钟时间,
  5. 没有看过,则获取该直播视频的免费时间根据活动ID,同时设置该直播视频的过期时间(只针对该用户自己哦),返回个模板,说:这个人可以观看的。
  6. 直播视频已经看过了,则不可以继续观看哦!嘻嘻...

二、免费观看时间原理:

  1. 客户打开直播视频,立马通过消息系统(WorkerMan)获取clientId(该直播页面唯一的票据).
  2. 用户点击播放按钮时候,Ajax异步Post一个数据到服务器(clientId、liveId、openId),绑定该用户的与直播视频,根据liveId在Redis获取该活动的免费过期时间expireTime,设置自己的基本信息存储到Reids中,哈希存储格式key:liveId+clientIp.
  3. 返回给直播模板200,客户可以观看啦!
  4. 这时候就会在服务器端有一个PHP脚本(psubscribe.php)在执行,执行方式一nohub,详情观看,他负责只要有key过期。这个脚本则会收到给key过期的事件(键空间通知的机制),该过期事件会返回一个回调函数(返回值:liveId+clientIp),然后我们在这个回调函数中调用一个平台的API接口,同时传递参数过去【liveId+clientIp】,该接口接受到参数后,立马想视频直播页面通过消息机制(workerman),发送一条消息,免费时间观看完了,马上关闭视频,这时候只要在发送通知信息的时候携带一个参数状态即可。检查权限一开始就会终止掉该权限检测和关闭该视频直播的操作(JS+css样式去实现可以了),客户没办法观看了,直播窗口立即弹出。
  5. 再次点击则无法观看
  6. 刷新浏览器:点击继续观看,异步到Redis查找根据liveId和clientIp查找是否具有权限观看。

直播基本Redis信息hash存储:[后台维护人员设置或者同步Redis]

 // 同步活动信息到Redis
    public function synchronizeLive()
    {
        $liveId = ‘L00735‘;
        $redis = new \Redis();
        $redis->connect(‘121.141.188.202‘, 63789);
        $setResult = $redis->hMset($liveId, [
            ‘liveId‘ => $liveId,
            ‘expireTime‘ => -1,        //过期时间设定
            ‘isLive‘ => 1,              //是否是直播,1.直播,0.非直播(历史回顾)
        ]);
        if($setResult == false ) exit(‘Hmset fail‘);
        exit(‘Hmset success‘);
    }

权限设置判断代码:

    /**
     * 活动模板页面,权限请求处理
     * 这个操作应该是客户端发起的请求,当客户点击播放按钮的时候,开始该活动的免费时间(10分钟)播放,到期立马关掉
     */
    public function checkAuth($liveId = ‘L00735‘, $clientId = ‘‘, $openId = ‘‘)
    {
        $redis = new \Redis();
        $redis->connect(‘121.41.88.209‘, 63789);
        //检查是否是直播,还是历史回顾视频的观看
        $isLive = $redis->hGet($liveId, ‘isLive‘);
        $clientIp = $this->getClientIp();
        if ($isLive == 1) //是直播 ,则这时候就会根据客户端的IP来判断,同一个IP则不可以继续观看啦
        {
            $watchTag = $redis->hGet($liveId . ‘:‘ . $clientIp, ‘watchTag‘); // 该值不存在,返回:false
            if ($watchTag == false) { // 没有看过的
                //用户给自己设置过期时间
                $expireTime = $redis->hGet($liveId, ‘expireTime‘);
                $redis->setex($liveId . ‘:‘ . $clientIp, $expireTime, $liveId . $expireTime);
                $redis->hMset($liveId . ‘:‘ . $clientIp, [
                    ‘liveId‘ => $liveId,
                    ‘clientIp‘ => $clientIp,   //客户端IP
                    ‘clientId‘ => $clientId,          //客户观看唯一id
                    ‘watchTag‘ => 1,           //观看标记:1.已经看过,0.一次都看过
                    ‘openId‘ => $openId,           //微信用户唯一标识

                ]);
                //JSON 返回信息
                $message = [
                    ‘status‘ => 200,
                    ‘liveId‘ => $liveId,
                    ‘clientId‘ => $clientId,
                    ‘watchTag‘ => 0,  // 0 表示没有观看过
                    ‘isLive‘ => $isLive,  // 是直播

                ];
                exit(json_encode($message)); // 您是第一来,可以观看的
            } else {
                $redisIp = $redis->hGet($liveId . ‘:‘ . $clientIp, ‘clientIp‘);
                if ($clientIp == $redisIp && $watchTag == 1) {
                    $message = [
                        ‘status‘ => 500,
                        ‘liveId‘ => $liveId,
                        ‘clientId‘ => $clientId,
                        ‘watchTag‘ => 1,  // 1 表示已经观看过了
                        ‘isLive‘ => $isLive,  // 是直播
                    ];
                    exit(json_encode($message)); //不可以继续观看了
                }
            }

        } else //不是直播,你可以继续观看,但是每次都是从头开始看哦
        {
            $watchTag = $redis->hGet($liveId . ‘:‘ . $clientIp, ‘watchTag‘); // 该值不存在,返回:false
            if ($watchTag == false)  // 您是第一来,可以观看的
            {
                //用户给自己设置过期时间
                $expireTime = $redis->hGet($liveId, ‘expireTime‘);
                $redis->setex($liveId . ‘:‘ . $clientIp, $expireTime, $liveId . $expireTime);
                $redis->hMset($liveId . ‘:‘ . $clientIp, [
                    ‘liveId‘ => $liveId,
                    ‘clientIp‘ => $this->getClientIp(),   //客户端IP
                    ‘clientId‘ => $clientId,          //客户观看唯一id
                    ‘watchTag‘ => 1,           //观看标记:1.已经看过,0.一次都看过
                    ‘openId‘ => $openId,           //微信用户唯一标识
                ]);
                //JSON 返回信息
                $message = [
                    ‘status‘ => 200,
                    ‘liveId‘ => $liveId,
                    ‘clientId‘ => $clientId,
                    ‘watchTag‘ => 0,  // 1 表示已经观看过了
                    ‘isLive‘ => 0,  // 是直播
                ];
                exit(json_encode($message));
            }
            // 您是第二次来了,自己给自己重新设置过期时间,从头开始播放吧
            $expireTime = $redis->hGet($liveId, ‘expireTime‘);
            $redis->setex($liveId . ‘:‘ . $clientIp, $expireTime, $liveId . $expireTime);
            $redis->hMset($liveId . ‘:‘ . $clientIp, [
                ‘liveId‘ => $liveId,
                ‘clientIp‘ => $this->getClientIp(),   //客户端IP
                ‘clientId‘ => $clientId,          //客户观看唯一id
                ‘watchTag‘ => 1,           //观看标记:1.已经看过,0.一次都看过
                ‘openId‘ => $openId,           //微信用户唯一标识
            ]);
            //========================统计该用户看过的次数以及刷新次数==========================
            $redis->incr($liveId . ‘:‘ . $this->getClientIp());
            //如果想知道用户在一年中每天的点击量,那么只要将用户 ID 以及相关的日期信息作为键
            $redis->incr(date(‘Y-m-d‘) . ‘:‘ . $clientId);
            //========================统计该用户看过的次数以及刷新次数==========================
            $message = [
                ‘status‘ => 200,
                ‘liveId‘ => $liveId,
                ‘clientId‘ => $clientId,
                ‘watchTag‘ => 1,  // 1 表示已经观看过了
                ‘isLive‘ => 0,  // 是直播
            ];
            exit(json_encode($message)); // 您是第一来,可以观看的

        }
    }

服务器API接口:

/**
     * 过期事件处理
     * 这个是一个key过期之后要执行的一个Api接口,同时携带liveId参数过来
     */
    public function expireEvent()
    {
        $liveId = $_POST[‘liveId‘];
        $redis = new \Redis();
        $redis->connect(‘121.141.188.209‘, 63789);
        //活动模板页面的消息处理
        $clientId = $redis->hGet($liveId, ‘clientId‘);
        $message = [
            ‘liveId‘ => $liveId,
            ‘clientId‘ => $clientId,
            ‘watchTag‘ => 1,  // 1 表示已经观看过了
            ‘openId‘ => $redis->hGet($liveId, ‘openId‘),
        ];
        Gateway::$registerAddress = ‘130.126.220.213:1238‘;
        Gateway::sendToClient($clientId, $message);
        $phone = ‘1366936*****‘;
        $code = rand(100000, 999999);
        $this->sendMessage($phone, $code);
    }

发送短信接口:

    /**
     * @param $smsMob
     * @param $code
     * @return mixed|string
     * 短信接口
     */
    public function sendMessage($smsMob, $code)
    {
        $uId = "userName";
        $key = "85fd6d7ab31231231214c557814df0";
        $smsText = "你以为你干的这些事情没人知道?把你传网上去,看你还有什么脸,回复验证码:" . $code . ",自己看";
        $url = ‘http://utf8.sms.webchinese.cn/?Uid=‘ . $uId . ‘&Key=‘ . $key . ‘&smsMob=‘ . $smsMob . ‘&smsText=‘ . $smsText;
        if (function_exists(‘file_get_contents‘)) {
            $file_contents = file_get_contents($url);
        } else {
            $ch = curl_init();
            $timeout = 5;
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
            $file_contents = curl_exec($ch);
            curl_close($ch);
        }
        return $file_contents;
    }

获取客户端IP地址:

    /**
     * 获取客户端IP
     */
    public function getClientIp()
    {
        $ip = ‘unknown‘;
        $unknown = ‘unknown‘;
        if (isset($_SERVER[‘HTTP_X_FORWARDED_FOR‘]) && $_SERVER[‘HTTP_X_FORWARDED_FOR‘] && strcasecmp($_SERVER[‘HTTP_X_FORWARDED_FOR‘], $unknown)) {
            // 使用透明代理、欺骗性代理的情况
            $ip = $_SERVER[‘HTTP_X_FORWARDED_FOR‘];
        } elseif (isset($_SERVER[‘REMOTE_ADDR‘]) && $_SERVER[‘REMOTE_ADDR‘] && strcasecmp($_SERVER[‘REMOTE_ADDR‘], $unknown)) {
            // 没有代理、使用普通匿名代理和高匿代理的情况
            $ip = $_SERVER[‘REMOTE_ADDR‘];
        }
        // 处理多层代理的情况
        if (strpos($ip, ‘,‘) !== false) {
            // 输出第一个IP
            $ip = reset(explode(‘,‘, $ip));
        }
        return $ip;
    }

待续.....

参考地址:

  1. https://segmentfault.com/a/1190000002890750
  2. http://www.imooc.com/article/10431
时间: 2024-10-06 10:55:01

Redis实践操作之—— 直播视频定时控制【TCP长连接框架(WorkerMan)+键空间通知的机制 ( Keyspace Notifications)+短信接口(API)】的相关文章

【Netty4 简单项目实践】六、断掉未鉴权的TCP长连接--ChannelHandelContext中的定时器用法

在TCP长连接模式下,我们需要及时释放那些未授权的TCP链接,让系统运行得更稳健一些. 首先是connect上来的TCP报文需要设置一个存活期,通过在pipleline上设置超时处理器ReadTimeoutHandler ch.pipeline().addLast(new ReadTimeoutHandler(120)); 使得一个TCP在120秒内没有收到数据就断掉. 这样做的目的是让连接者必须发TCP报文才能维持连接. 下一步在业务层对ChannelHandlerContext进行鉴权.与H

redis中键空间通知

通过redis的键空间通知,当redis删除过期key的时候,及时更新mongodb数据库中user的状态 var Redis = require('ioredis'); var redis = new Redis(); redis.subscribe('[email protected]__:expired',function(err,count){}) //订阅key过期事件 redis.on('message',function(channel,message){ //对redis的操作的

极限优化:php巧用tcp长连接

极限优化:php巧用tcp长连接 提交 我的评论 加载中 已评论 极限优化:php巧用tcp长连接 2015-01-23 架构师之路 架构师之路 架构师之路 微信号 功能介绍 通往架构师之路,悠远而漫长,一路上,我们同行. 上一期,和大家分享了YouTube系统架构,本期将和大家分享一个大并发下php使用tcp长连接访问后端的优化方法. php巧用TCP长连接优化 一.面向人群如果你的站点架构满足以下几点,那么本文的优化方案会非常适合你:1)使用php等脚本语言作为开发语言2)需要连接后端服务,

TCP长连接与短链接

1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的 经典的三次握手示意图: 经典的四次握手关闭图: 2. TCP短连接我们模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接.client向server发送消息,ser

TCP长连接与短连接的区别

1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的 经典的三次握手示意图: 经典的四次握手关闭图: 2. TCP短连接 我们模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接.client向server发送消息,se

Redis 键空间通知

[Redis 键空间通知] 键空间通知使得客户端可以通过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件. 以下是一些键空间通知发送的事件的例子: 所有修改键的命令. 所有接收到 LPUSH 命令的键. 0 号数据库中所有已过期的键. 事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发, 因此所有支持订阅与发布功能的客户端都可以在无须做任何修改的情况下, 直接使用键空间通知功能. 因为 Redis 目前的订阅与发布功能采取的是发送即忘(fire and fo

TCP长连接和短连接

1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的 经典的三次握手示意图: 经典的四次握手关闭图: 2. TCP短连接 我们模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接.client向server发送消息,se

redis 基本操作-python 使用redis-手机验证接口-发送短信接口

目录 复习 今日内容 redis python使用redis 手机验证接口 发送短信接口 复习 """ 1.git项目开发 提供公钥成为开发者.copy项目.开发项目 先commit.再pull(可能出现冲突).最后push 特殊功能可以新建dev的子分支进行开发:git checkout -b 子分支, 切换回dev分支合并子分支内容:git merge 子分支 2.短信 注册并申请通信短信服务应用 安装指定模块,根据申请的应用配置得到发送短信的对象,对象调用方法完成短信的

基于Apache mina 的android 客户端tcp长连接实现

TCP-long-connection-based-on-Apache-mina 基于Apache mina 的tcp长连接实现,可用于android客户端推送. 项目Github地址:https://github.com/sddyljsx/Android-tcp-long-connection-based-on-Apache-mina 项目将Apache的mina项目移植到了android平台.实现长连接的主要思想是使用了mina的KeepAliveFilter过滤器. acceptor.ge