• Redis应用----消息传递


    1、摘要

      消息传递这一应用广泛存在于各个网站中,这个功能也是一个网站必不可少的。常见的消息传递应用有,新浪微博中的@我呀、给你评论然后的提示呀、赞赞赞提示、私信呀、甚至是发微博分享的新鲜事;知乎中的私信呀、live发送过来的消息、知乎团队消息呀等等。

    2、实现方法

      消息传递即两个或者多个客户端在相互发送和接收消息。

      通常有两种方法实现:

      第一种为消息推送。Redis内置有这种机制,publish往频道推送消息、subscribe订阅频道。这种方法有一个缺点就是必须保证接收者时刻在线(即是此时程序不能停下来,一直保持监控状态,假若断线后就会出现客户端丢失信息)

      第二种为消息拉取。所谓消息拉取,就是客户端自主去获取存储在服务器中的数据。Redis内部没有实现消息拉取这种机制。因此我们需要自己手动编写代码去实现这个功能。

      在这里我们,我们进一步将消息传递再细分为一对一的消息传递,多对多的消息传递(群组消息传递)。

    【注:两个类的代码相对较多,因此将其折叠起来了】

    3、一对一消息传递

      例子1:一对一消息发送与获取

      模块要求:

      1、提示有多少个联系人发来新消息

      2、信息包含发送人、时间、信息内容

      3、能够获取之前的旧消息

      4、并且消息能够保持7天,过期将会被动触发删除

      Redis实现思路:

      1、新消息与旧消息分别采用两个链表来存储

      2、原始消息的结构采用数组的形式存放,并且含有发送人、时间戳、信息内容

      3、在推入redis的链表前,需要将数据转换为json类型然后再进行存储

      4、在取出新信息时应该使用rpoplpush来实现,将已读的新消息推入旧消息链表中

      5、取出旧消息时,应该用旧消息的时间与现在的时间进行对比,若超时,则直接删除后面的全部数据(因为数据是按时间一个一个压进链表中的,所以对于时间是有序排列的)

    数据存储结构图:

     

    PHP的实现代码:

    #SinglePullMessage.class.php

    复制代码
      1 <?php
      2 #单接接收者接收消息
      3 class SinglePullMessage
      4 {
      5     private $redis='';  #存储redis对象
      6     /**
      7     * @desc 构造函数
      8     * 
      9     * @param $host string | redis主机
     10     * @param $port int    | 端口
     11     */
     12     public function __construct($host,$port=6379)
     13     {
     14         $this->redis=new Redis();
     15         $this->redis->connect($host,$port);
     16     } 
     17 
     18     /**
     19     * @desc 发送消息(一个人)
     20     * 
     21     * @param $toUser   string    | 接收人
     22     * @param $messageArr array   | 发送的消息数组,包含sender、message、time 
     23     *
     24     * @return bool
     25     */
     26     public function sendSingle($toUser,$messageArr)
     27     {
     28         $json_message=json_encode($messageArr);    #编码成json数据
     29         return $this->redis->lpush($toUser,$json_message);      #将数据推入链表 
     30     }
     31 
     32     /**
     33     * @desc 用户获取新消息
     34     *
     35     * @param $user string | 用户名
     36     *
     37     * @return array 返回数组,包含多少个用户发来新消息,以及具体消息
     38     */
     39     public function getNewMessage($user)
     40     {
     41         #接收新信息数据,并且将数据推入旧信息数据链表中,并且在原链表中删除
     42         $messageArr=array();
     43         while($json_message=$this->redis->rpoplpush($user, 'preMessage_'.$user))
     44         {
     45             $temp=json_decode($json_message);   #将json数据变成对象
     46             $messageArr[$temp->sender][]=$temp;        #转换成数组信息
     47         }
     48         if($messageArr)
     49         {
     50             $arr['count']=count($messageArr);   #统计有多少个用户发来信息
     51             $arr['messageArr']=$messageArr;
     52             return $arr;
     53         }
     54         return false;
     55     }
     56 
     57     public function getPreMessage($user)
     58     {
     59         ##取出旧消息
     60         $messageArr=array();
     61         $json_pre=$this->redis->lrange('preMessage_'.$user, 0, -1);    #一次性将全部旧消息取出来
     62         foreach ($json_pre as $k => $v) 
     63         {
     64             $temp=json_decode($v);            #json反编码
     65             $timeout=$temp->time+60*60*24*7;  #数据过期时间  七天过期
     66             if($timeout<time())               #判断数据是否过期
     67             {
     68                 if($k==0)                     #若是最迟插入的数据都过期了,则将所有数据删除
     69                 {
     70                     $this->redis->del('preMessage_'.$user);
     71                     break;
     72                 }
     73                 $this->redis->ltrim('preMessage_'.$user, 0, $k);  #若检测出有过期的,则将比它之前插入的所有数据删除
     74                 break;
     75             }
     76             $messageArr[$temp->sender][]=$temp;
     77         }
     78         return $messageArr;
     79     }
     80 
     81     /**
     82     * @desc 消息处理,没什么特别的作用。在这里这是用来处理数组信息,然后将其输出。 
     83     *
     84     * @param $arr array | 需要处理的信息数组
     85     *
     86     * @return 返回打印输出
     87     */
     88     public function dealArr($arr)
     89     {
     90         foreach ($arr as $k => $v) 
     91         {
     92             foreach ($v as $k1 => $v2) 
     93             {
     94                 echo '发送人:'.$v2->sender.'    发送时间:'.date('Y-m-d h:i:s',$v2->time).'<br/>';
     95                 echo '消息内容:'.$v2->message.'<br/>';
     96             }
     97             echo "<hr/>";
     98         }
     99     }
    100 
    101 
    102 }
    复制代码

     测试:

      1、发送消息

      #建立test1.php

    复制代码
    1 include './SinglePullMessage.class.php';
    2 $object=new SinglePullMessage('192.168.95.11');
    3 #发送消息
    4 $sender='boss';     #发送者
    5 $to='jane';         #接收者
    6 $message='How are you';    #信息
    7 $time=time();
    8 $arr=array('sender'=>$sender,'message'=>$message,'time'=>$time);
    9 echo $object->sendSingle($to,$arr);
    复制代码

      2、获取新消息

      #建立test2.php

    复制代码
     1 include './SinglePullMessage.class.php';
     2 $object=new SinglePullMessage('192.168.95.11');
     3 #获取新消息
     4 $arr=$object->getNewMessage('jane');
     5 if($arr)
     6 {
     7     echo $arr['count']."个联系人发来新消息<br/><hr/>";
     8     $object->dealArr($arr['messageArr']);   
     9 }
    10 else
    11     echo "无新消息";
    复制代码

       访问结果:

      3、获取旧消息

      #建立test3.php

    复制代码
     1 include './SinglePullMessage.class.php';
     2 $object=new SinglePullMessage('192.168.95.11');
     3 #获取旧消息
     4 $arr=$object->getPreMessage('jane');
     5 if($arr)
     6 {
     7     $object->dealArr($arr);
     8 }
     9 else
    10     echo "无旧数据";
    复制代码

    4、多对多消息传递

      例子2:多对多消息发送与获取(即是群组)

      模块要求:

      1、用户能够自行创建群组,并成为群主

      2、群主可以拉人进来作为群组成员、并且可以踢人

      3、用户可以直接退出群组

      4、可以发送消息,每一位成员都可以拉取消息

      5、群组的消息最大容纳量为5000条

      6、成员可以拉取新消息,并提示有多少新消息

      7、成员可以分页获取之前已读的旧消息

      。。。。。功能就写这几个吧,有需要或者想练习的同学们可以增加其他功能,例如禁言、匿名消息发送、文件发送等等。

      Redis实现思路:

       1、群组的消息以及群组的成员组成采用有序集合进行存储。群组消息有序集合的member存储用户发送的json数据消息,score存储唯一值,将采用原子操作incr获取string中的自增长值进行存储;群组成员有序集合的member存储user,score存储非零数字(在这里这个score意义不大,我的例子代码中使用数字1为群主的score,其他的存储为2。当然这使用这个数据还可以扩展别的功能,例如群组中成员等级)可参考下面数据存储结构简图。

      2、用户所加入的群组也是采用有序集合进行存储。其中,member存储群组ID,score存储用户已经获取该群组的最大消息分值(对应群组消息的score值)

      3、用户创建群组的时候,通过原子操作incr从而获取一个唯一ID

      4、用户在群中发送消息时,也是通过原子操作incr获取一个唯一自增长有序ID

      5、在执行incr时,为防止并发导致竞争关系,因此需要进行加锁操作【redis详细锁的讲解可以参考:Redis构建分布式锁http://www.cnblogs.com/phpstudy2015-6/p/6575775.html

      6、创建群组方法简要思路,任何一个用户都可以创建群组聊天,在创建的同时,可以选择时是否添加群组成员(参数通过数组的形式)。创建过程将会为这个群组建立一个群组成员有序集合(群组信息有序集合暂时不创建),接着将群主添加进去,再将群ID添加用户所参加的群组有序集合中。

    数据存储结构图:

    PHP的代码实现:

    #ManyPullMessage.class.php

     View Code

    测试:

      1、建立createGroupChat.php(测试创建群组功能)

      执行代码并创建568、569群组(群主为jack)

    复制代码
    1 include './ManyPullMessage.class.php';
    2 $object=new ManyPullMessage('192.168.95.11');
    3 #创建群组
    4 $user='jack';
    5 $arr=array('jane1','jane2');
    6 $a=$object->createGroupChat($user,$arr);
    7 echo "<pre>";
    8 print_r($a);
    9 echo "</pre>";die;
    复制代码

     

      2、建立addMembers.php(测试添加成员功能)

      执行代码并添加新成员

    1 include './ManyPullMessage.class.php';
    2 $object=new ManyPullMessage('192.168.95.11');
    3 $b=$object->addMembers('jack','568',array('jane1','jane2','jane3','jane4'));
    4 echo "<pre>";
    5 print_r($b);
    6 echo "</pre>";die;

      3、建立delete.php(测试群主删除成员功能)

    复制代码
    1 include './ManyPullMessage.class.php';
    2 $object=new ManyPullMessage('192.168.95.11');
    3 #群主删除成员
    4 $c=$object->delMembers('jack', '568', array('jane1','jane4'));
    5 echo "<pre>";
    6 print_r($c);
    7 echo "</pre>";die;
    复制代码

      4、建立sendMessage.php(测试发送消息功能)

      多执行几遍,568、569都发几条

    复制代码
     1 include './ManyPullMessage.class.php';
     2 $object=new ManyPullMessage('192.168.95.11');
     3 #发送消息
     4 $user='jane2';
     5 $message='go go go';
     6 $groupChatID=568;
     7 $arr=array('sender'=>$user, 'message'=>$message, 'time'=>time());
     8 $d=$object->sendMessage($user,$groupChatID,$arr);
     9 echo "<pre>";
    10 print_r($d);
    11 echo "</pre>";die;
    复制代码

      5、建立getNewMessage.php(测试用户获取新消息功能)

    复制代码
    1 include './ManyPullMessage.class.php';
    2 $object=new ManyPullMessage('192.168.95.11');
    3 #用户获取新消息
    4 $e=$object->getNewMessage('jane2');
    5 echo "<pre>";
    6 print_r($e);
    7 echo "</pre>";die;
    复制代码

      6、建立getPartMessage.php(测试用户获取某个群组部分消息)

      (多发送几条消息,用于测试。568中共18条数据)

    复制代码
    1 include './ManyPullMessage.class.php';
    2 $object=new ManyPullMessage('192.168.95.11');
    3 #用户获取某个群组部分消息
    4 $f=$object->getPartMessage('jane2', 568, 1, 10); 
    5 echo "<pre>";
    6 print_r($f);
    7 echo "</pre>";die;
    复制代码

    page=1,size=10

    page=2,size=10

    测试完毕,还需要别的功能可以自己进行修改添加测试。

    这次整理这篇文章相对比较赶,心里已经想着快点整理完赶紧学习其他的技术啦,哈哈22333。各位大神请留步,恳请各位给点学习redis的指导意见,本人职业方向是PHP

  • 相关阅读:
    python中重要的模块--asyncio
    Mysql连接报错:1130-host ... is not allowed to connect to this MySql server如何处理
    驱动精灵扩展版(集成万能网卡驱动)无法自动识别网卡的解决方案
    ROS HTB限速失败原因分析和需注意事项
    按键精灵saystring无法使用的几种解决方案
    ESXI5-WIN2008R2安装域控以及额外域笔记
    centos如何使用utc时间
    NFS的安装以及windows/linux挂载linux网络文件系统NFS
    用命令行方式关闭CentOS防火墙
    CentOS6.4 上搭建NIS网络信息服务器
  • 原文地址:https://www.cnblogs.com/yzycoder/p/6775337.html
Copyright © 2020-2023  润新知