Messaging This application is widely used in various websites, and this function is also necessary for a website. This paper mainly introduces the application of Redis in PHP--message passing.
Read Catalogue
1. Abstract
2. Realization method
3, a one-message delivery
4, many-to-many message delivery
1. Abstract
Messaging This application is widely used in various websites, and this function is also necessary for a website. Common messaging applications have, Sina Weibo @ i ah, give you comments and then the tips Ah, praise like the hint, a private message, or even a tweet to share the new, know the private message Ah, live sent over the news, know the team news and so on.
2. Realization method
Message passing is two or more clients sending and receiving messages to each other.
There are usually two ways to achieve this:
The first is a message push. Redis has this mechanism built in, publish push messages to channel, subscribe subscribe to channels. One drawback of this approach is the need to ensure that the receiver is always online (that is, the program can not stop at this time, keep the monitoring state, if the disconnection will occur after the client lost information)
the second type is for messages to be pulled. the so-called message pull is that the client autonomously to get the data stored in the server. There is no mechanism to implement message fetching within Redis. So we need to manually write code to implement this function.
Here we further subdivided message delivery into one-to-one messaging, many-to-many messaging (group messaging).
"Note: The code for two classes is relatively large, so it's folded up"
3, a one-message delivery
Example 1: A pair of messages sent and fetched
Module requirements:
1, how many contacts to send a new message
2, the information contains the sender, time, information content
3. Ability to get old messages
4, and the message can be kept for 7 days, the expiration will trigger the deletion passively
Redis Implementation ideas:
1, the new message and the old message, respectively, the use of two linked lists to store
2, the structure of the original message is stored in the form of an array, and contains the sender, timestamp, information content
3. Before pushing into the list of Redis, you need to convert the data to JSON type before storing it.
4. When removing new information, it should be implemented using Rpoplpush to push the read new message into the old message list
5, the old message should be removed, the time of the old message should be compared with the current time, if time-out, then directly delete all the data behind (because the data is pressed into the list by time, so for the time is ordered)
Data storage structure diagram:
PHP Implementation code:
#SinglePullMessage. class.php
<?php# receiver receives the message class singlepullmessage{private $redis = '; #存储redis对象/** * @desc Constructor * * @param $host string | redis Host * @param $port int | Port */Public function __construct ($host, $port =6379) {$this->redis=new redis (); $this->redis->connect ($host, $ Port); }/** * @desc send message (one person) * * @param $toUser String | Recipient * @param $MESSAGEARR Array | An array of messages sent, containing sender, message, TIME * * @return BOOL */Public Function Sendsingle ($toUser, $MESSAGEARR) {$json _message=json_e Ncode ($MESSAGEARR); #编码成json数据 return $this->redis->lpush ($toUser, $json _message); #将数据推入链表}/** * @desc users get new messages * * @param $user string | User name * @return Array returns an array of how many users sent a new message, and a specific message */Public function getnewmessage ($user) {#接收新信息数据, and pushes the data into the old information data Link list, and in the original linked list Delete $messageArr =array (); while ($json _message= $this->redis->rpoplpush ($user, ' Premessage_ '. $user)) {$temp =json_decode ($json _message) ; #将json数据变成对象 $MESSAGEARR [$temp->sender][]= $temp; #转换成数组信息} if ($MESSAGEARR) {$arr [' Count ']=count ($MESSAGEARR); #统计有多少个用户发来信息 $arr [' Messagearr ']= $messageArr; return $arr; } return false; The Public Function Getpremessage ($user) {# #取出旧消息 $MESSAGEARR =array (); $json _pre= $this->redis->lrange (' Premessage_ '. $user, 0,-1); #一次性将全部旧消息取出来 foreach ($json _pre as $k + = $v) {$temp =json_decode ($v); #json反编码 $timeout = $temp->time+60*60*24*7; #数据过期时间 seven-day expiration if ($timeout <time ()) #判断数据是否过期 {if ($k ==0) #若是最迟插入的数据都过期了, all data is deleted {$this->redis->del (' PreM Essage_ '. $user); Break } $this->redis->ltrim (' Premessage_ '. $user, 0, $k); #若检测出有过期的, the break is removed from all data that was inserted before it; } $MESSAGEARR [$temp->sender][]= $temp; } return $MESSAGEARR; }/** * @desc message processing, no special effect. Here this is used to process the array information and then output it. * * @param $arr Array | Array of information to be processed * * @return return printout */Public Function Dealarr ($arr) {foreach ($arr as $k + $v) {foreach ($v as $k 1 = $v 2) {echo ' sender: '. $v 2->sender. ' Send time: '. Date (' y-m-d h:i:s ', $v 2->time). ' <br/> '; Echo ' message content: '. $v 2->message. ' <br/> '; } echo "
Test:
1. Send a message
#建立test1. php
Include './singlepullmessage.class.php '; $object =new singlepullmessage (' 192.168.95.11 '); #发送消息 $sender = ' boss '; #发送者 $to = ' Jane '; #接收者 $message = ' How is you '; #信息 $time =time (); $arr =array (' sender ' and $sender, ' message ' + $message, ' time ' = $time); Echo $object Sendsingle ($to, $arr);
2. Get new messages
#建立test2. php
Include './singlepullmessage.class.php '; $object =new singlepullmessage (' 192.168.95.11 '); #获取新消息 $arr = $object Getnewmessage (' Jane '), if ($arr) {echo $arr [' count ']. " A new message from the contact person <br/>
Access results:
3. Get Old messages
#建立test3. php
Include './singlepullmessage.class.php '; $object =new singlepullmessage (' 192.168.95.11 '); #获取旧消息 $arr = $object Getpremessage (' Jane '), if ($arr) {$object->dealarr ($arr);} else echo "no old data";
4, many-to-many message delivery
Example 2: Multiple-to-multi-message sending and fetching (that is, a group)
Module requirements:
1, users can create their own groups, and become a group master
2, the group master can pull people in as a group member, and can kick people
3, the user can directly exit the group
4, can send a message, each member can pull messages
5, the group's message maximum capacity is 5,000
6. Members can pull new messages and indicate how many new messages they have
7. Members can be paged to get old messages that were previously read
。。。。。 function to write these, you need or want to practice the students can add other features, such as the ban, anonymous message sending, file sending and so on.
Redis Implementation ideas:
1. Group messages and group members are stored in an ordered set. The member of the group message ordered collection stores the JSON data messages sent by the user, score stores the unique values, takes the atomic operation INCR to obtain the self-growth value in the string, stores the user in the member of the group member's ordered collection, Score store non-0 numbers (here This score doesn't make much sense, my example code uses the number 1 as the group master score, the other storage is 2. Of course, this data can also be used to extend other functions, such as group membership level, you can refer to the following data storage structure diagram.
2, the user joins the group also uses the orderly collection carries on the storage. Where member storage Group Id,score Storage user has obtained the maximum message score for the group (score value for the corresponding group message)
3, when the user creates the group, by atomic Operation INCR to obtain a unique ID
4, when the user sends the message in the group, also obtains a unique self-growing ordered ID by atomic operation incr
5, in the execution of INCR, in order to prevent the concurrency caused by competition, so the need to lock the operation "Redis detailed lock explanation can be consulted: Redis build distributed Lock http://www.jb51.net/article/109704.htm"
6, create the group method of a brief idea, any one user can create group chat, while creating, you can choose whether to add group members (parameters through the form of an array). The creation process will create an ordered set of group members for this group (the group information ordered collection is temporarily not created), then add the group owner, and then add the group ID to the group organized by the user to participate in the collection.
Data storage structure diagram:
PHP's Code implementation:
#ManyPullMessage. class.php
<?phpclass manypullmessage{Private $redis = "; #存储redis对象/** * @desc Constructor * * @param $host string | Redis host * @param $port int | Port */Public function __construct ($host, $port =6379) {$this->redis=new redis (); $this->redis->connect ($host, $ Port); }/** * @desc The method used to create the group, you can also pull people into the group * * @param $user string when created | User name, create Group owner * @param $addUser Array | Array of other users * * @param $lockName String | The name of the lock, used to get the group ID, use * @return int to return the group ID */Public Function creategroupchat ($user, $addUser =array (), $lockName = ' Chatidlock ') {$identifier = $this->getlock ($lockName), #获取锁 if ($identifier) {$id = $this->redis->incr (' Groupchatid '); #获取群组ID $this->releaselock ($lockName, $identifier); #释放锁} else return false; $messageCount = $this->redis->set (' Countmessage_ '. $id, 0); #初始化这个群组消息计数器 #开启非事务型流水线, pass all Redis commands to redis at once, reducing the connection to redis $pipe = $this->redis->pipeline (); $this->redis->zadd (' Groupchat_ '. $id, 1, $user); #创建群组成员有序集合, and add the group master #将这个群组添加到user所参加的群组有序集合中 $this->redis->zAdd (' Hasgroupchat_ '. $user, 0, $id); foreach ($addUser as $v) #创建群组的同时需要添加的用户成员 {$this->redis->zadd (' Groupchat_ '. $id, 2, $v); $this->redis->zadd (' Hasgroupchat_ '. $v, 0, $id); } $pipe->exec (); return $id; #返回群组ID}/** * @desc group master active Pull-in Group * * @param $user string | Group master name * @param $groupChatID int | Group ID * @param $addMembers array | User who needs to pull into the group * * @return BOOL */Public Function addmembers ($user, $groupChatID, $addMembers =array ()) {$groupMasterScore = $thi S->redis->zscore (' Groupchat_ '. $groupChatID, $user); #将groupChatName的群主取出来 if ($groupMasterScore ==1) #判断user是否是群主 {$pipe = $this->redis->pipeline (); #开启非事务流水线 foreach ($addMembers as $v) {$this->redis->zadd (' Groupchat_ '. $groupChatID, 2, $v); #添加进群 $this->redis->zadd (' Hasgroupchat_ '. $v, 0, $groupChatID); #添加群名到用户的有序集合中} $pipe->exec (); return true; } return false; }/** * @desc Group master Delete member * * @param $user string | Group master name * @param $groupChatID int | Group ID * @param $delMembers array | Name of member to delete * * @retuRN BOOL */Public Function delmembers ($user, $groupChatID, $delMembers =array ()) {$groupMasterScore = $this->redis- >zscore (' Groupchat_ '. $groupChatID, $user); if ($groupMasterScore ==1) #判断user是否是群主 {$pipe = $this->redis->pipeline (); #开启非事务流水线 foreach ($delMembers as $v) { $this->redis->zrem (' Groupchat_ ' $groupChatID, $v); $this->redis->zrem (' Hasgroupchat_ ' $v, $groupChatID); } $pipe->exec (); return true; } return false; }/** * @desc exit Group * * @param $user string | User name * @param $groupChatID int | Group Name */Public Function quitgroupchat ($user, $groupChatID) {$this->redis->zrem (' Groupchat_ '. $groupChatID, $user) ; $this->redis->zrem (' Hasgroupchat_ ' $user, $groupChatID); return true; /** * @desc Send Message * * @param $user string | User name * @param $groupChatID int | Group ID * @param $MESSAGEARR array | An array containing the Sent messages * @param $preLockName string | Group message lock prefix, group message lock full name is Countlock_ Group ID * * @return BOOL */Public Function sendMessage ($user, $groupChatID, $MESSAGEARR, $preLOckname= ' Countlock_ ') {$memberScore = $this->redis->zscore (' Groupchat_ '. $groupChatID, $user); #成员score if ($ Memberscore) {$identifier = $this->getlock ($preLockName. $groupChatID), #获取锁 if ($identifier) #判断获取锁是否成功 {$messageCo unt= $this->redis->incr (' Countmessage_ '. $groupChatID); $this->releaselock ($preLockName. $groupChatID, $identifier); #释放锁} else return false; $json _message=json_encode ($MESSAGEARR); $this->redis->zadd (' Groupchatmessage_ ' $groupChatID, $messageCount, $json _message); $count = $this->redis->zcard (' Groupchatmessage_ '. $groupChatID); #查看信息量大小 if ($count >5000) #判断数据量有没有达到5000条 {#数据量超5000, you need to clear the old data $start =5000-$count; $this->redis->zremrangebyrank (' Groupchatmessage_ ' $groupChatID, $start, $count); } return true; } return false; }/** * @desc get new information * * @param $user string | Username * * @return successfully put back the JSON data array, no new information returned false */Public function getnewmessage ($user) {$arrID = $this->redis->zrange (' Hasgroupchat_ '. $user, 0,-1, ' WithscoreS '); #获取用户拥有的群组ID $json _message=array (); #初始化 foreach ($arrID as $k + $v) #遍历循环所有群组 to see if there is a new message {$messageCount = $this->redis->get (' Countmessage_ '. $k); #群组 Maximum Information score if ($messageCount > $v) #判断用户是否存在未读新消息 {$json _message[$k] [' message ']= $this->redis->zrangebyscore (' Groupchatmessage_ '. $k, $v +1, $messageCount); $json _message[$k] [' Count ']=count ($json _message[$k [' message ']); #统计新消息数量 $this->redis->zadd (' Hasgroupchat_ ' $user, $messageCount, $k); #更新已获取消息}} if ($json _message) return $json _message; return false; }/** * @desc page to get group information * * @param $user string | User name * @param $groupChatID int | Group ID * @param $page int | Page * @param $size int | How many data per page * * @return successfully returned JSON data, failed to return false */Public function getpartmessage ($user, $groupChatID, $page =1, $size =10) {$star t= $page * $size-$size; #开始截取数据位置 $stop = $page * $size-1; #结束截取数据位置 $json _message= $this->redis->zrevrange (' Groupchatmessage_ ' $groupChatID, $start, $stop); if ($json _message) return $json _message; return False; }/** * @desc Lock Method * * @param $lockName String | The name of the lock * @param $timeout int | Lock Expiration Time * * @return successful return identifier/failure returns false */Public function Getlock ($lockName, $timeout =2) {$identifier =uniqid (); #获取唯 An identifier $timeout =ceil ($timeout); #确保是整数 $end =time () + $timeout; while (Time () < $end) #循环获取锁 {/* #这里的set操作可以等同于下面那个if操作, and can be reduced once with redis communication if ($this->redis->set ($lockName, $ Identifier Array (' NX ', ' ex ' = + $timeout)) return $identifier; */if ($this->redis->setnx ($lockName, $identifier)) #查看 $lockName is locked {$this->redis->expire ($lockName, $ Timeout); #为 $lockName Set the expiration time to return $identifier; #返回一维标识符} elseif ($this->redis->ttl ($lockName) ===-1) {$this->redis->expire ($lockName, $timeout); #检测是否有 Set expiration time, no add} usleep (0.001); #停止0.001ms} return false; }/** * @desc release lock * * @param $lockName String | Lock name * @param $identifier String | Unique value of the lock * * @param bool */Public Function ReleaseLock ($lockName, $identifier) {if ($this->redis->get ($lockName) ==$ IdenTifier) #判断是锁有没有被其他客户端修改 {$this->redis->multi (); $this->redis->del ($lockName); #释放锁 $this->redis->exec (); return true; } else {return false; #其他客户端修改了锁, cannot delete someone else's lock}}}?>
Test:
1, establish creategroupchat.php (test create Group function)
Execute code and create 568, 569 groups (group master is Jack)
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); #创建群组 $user = ' Jack '; $arr =array ( ' Jane1 ', ' jane2 '); $a = $object->creategroupchat ($user, $arr); echo "<pre>";p rint_r ($a); echo "</pre>"; Die
2, establish addmembers.php (test add member function)
Execute code and add new members
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); $b = $object->addmembers (' Jack ', ' 568 ', Array (' jane1 ', ' jane2 ', ' jane3 ', ' jane4 ')); echo "<pre>"; Print_r ($b); echo "</pre>";d ie;
3, establish delete.php (test group master Delete member function)
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); #群主删除成员 $c = $object Delmembers (' Jack ', ' 568 ', Array (' jane1 ', ' jane4 '); echo "<pre>";p Rint_r ($c); echo "</pre>";d ie;
4, establish sendmessage.php (test send Message function)
Execute several times, 568, 569 are issued several
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); #发送消息 $user = ' jane2 '; $message = ' Go Go go '; $groupChatID =568; $arr =array (' sender ' and ' $user, ' message ' = $message, ' time ' =>time ()); $d = $object- >sendmessage ($user, $groupChatID, $arr); echo "<pre>";p Rint_r ($d); echo "</pre>";d ie;
5, establish getnewmessage.php (test user to get new message function)
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); #用户获取新消息 $e = $object Getnewmessage (' Jane2 '), echo "<pre>";p Rint_r ($e); echo "</pre>";d ie;
6, Establish getpartmessage.php (test user to get a group part of the message)
(Send a few more messages for testing.) 568 CPC 18 data)
Include './manypullmessage.class.php '; $object =new manypullmessage (' 192.168.95.11 '); #用户获取某个群组部分消息 $f = $object Getpartmessage (' Jane2 ', 568, 1, 10); echo "<pre>";p Rint_r ($f); echo "</pre>";d ie;
page=1,size=10
page=2,size=10
After the test is complete, additional functionality is required to modify the test itself.
The above is the whole content of this article, I hope that everyone's study has helped.