Redis application in php-message transmission, phpredis-message
Reading directory
1. Summary
2. Implementation Method
3. One-to-one message transmission
4. Multiple-to-multiple message transmission
1. Summary
Message Passing is widely used in various websites. This function is also essential for a website. Common messaging applications include @ me in Sina Weibo, comments to you, thumbs up, private messages, and even new things shared on Weibo; zhihu private messages, messages sent from live, And zhihu team messages.
2. Implementation Method
Two or more clients send and receive messages to each other.
There are two methods to achieve this:
The first type is message push.Redis has a built-in mechanism in which publish pushes messages to channels and subscribe subscription channels. One disadvantage of this method is that the receiver must be always online (that is, the program cannot be stopped at this time, and the monitoring status is always maintained. If the client loses information after the disconnection)
The second type is message pulling.Message pulling means that the client obtains data stored on the server. Redis does not implement this mechanism internally. Therefore, we need to manually write the code to implement this function.
Here, we further divide the message transmission into one-to-one message transmission and multiple-to-many message transmission (group message transmission ).
[Note: The two classes have a relatively large amount of code, so they are folded up]
3. One-to-one message transmission
Example 1: one-to-one message sending and Retrieval
Module requirements:
1. How many contacts have sent new messages?
2. The information includes the sender, time, and content.
3. Get old messages
4. The message can be kept for 7 days. Expired messages will be deleted passively.
Implementation of Redis:
1. New messages and old messages are stored in two linked lists respectively.
2. The structure of the original message is stored in an array and contains the sender, timestamp, and information.
3. Before pushing data to the redis linked list, you need to convert the data to the json type before storing it.
4. Use rpoplpush to retrieve new messages and push the read messages to the old linked list.
5. When the old message is retrieved, the time of the old message should be compared with the current time. If it times out, delete all the subsequent data directly (because the data is pressed into the linked list one by time, so the time is ordered)
Data storage structure:
PHP implementation code:
# SinglePullMessage. class. php
<? Php # class SinglePullMessage received by a single receiver {private $ redis = ''; # store redis object/*** @ 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 a message (one person) *** @ param $ toUser string | recipient * @ param $ messageArr array | array of sent messages, include sender, message, time ** @ return bool */public fun Ction sendSingle ($ toUser, $ messageArr) {$ json_message = json_encode ($ messageArr); # encode it into json data return $ this-> redis-> lpush ($ toUser, $ json_message); # Push data to the linked list}/*** @ desc user to get the new message ** @ param $ user string | user name ** @ return array returns the array, how many users send new messages and specific messages */public function getNewMessage ($ user) {# receive new information data and push the data into the old information data link table, and delete $ messageArr = array (); while ($ json_message = $ this-> redis-> rpoplpush ($ user, 'Prem Essage _'. $ user) {$ temp = json_decode ($ json_message); # convert json data into an object $ messageArr [$ temp-> sender] [] = $ temp; # convert to array information} if ($ messageArr) {$ arr ['Count'] = count ($ messageArr ); # count how many users send messages $ arr ['messagearr'] = $ messageArr; return $ arr;} return false;} public function getPreMessage ($ user) {## retrieve old message $ messageArr = array (); $ json_pre = $ this-> redis-> lrange ('premessage _'. $ user, 0,-1); # retrieve all old messages at a time ($ json_p Re as $ k => $ v) {$ temp = json_decode ($ v); # json anti-encoding $ timeout = $ temp-> time + 60*60*24*7; # data expiration time: seven days if ($ timeout <time () # determine whether the data has expired {if ($ k = 0) # if the latest inserted data has expired, delete all data {$ this-> redis-> del ('premessage _'. $ user); break;} $ this-> redis-> ltrim ('premessage _'. $ user, 0, $ k); # If any expired data is detected, the break will be deleted from all data inserted before it ;} $ messageArr [$ temp-> sender] [] = $ temp;} return $ messageArr;}/*** @ desc message processing has no special effect. Here this is used to process the array information and output it. ** @ Param $ arr array | array of information to be processed ** @ return returns Print Output */public function dealArr ($ arr) {foreach ($ arr as $ k = >$ v) {foreach ($ v as $ k1 = >$ v2) {echo 'sender :'. $ v2-> sender. 'sending time :'. date ('Y-m-d h: I: s', $ v2-> time ). '<br/>'; echo 'message content :'. $ v2-> message. '<br/>';} echo "
Test:
1. Send messages
# Create test1.php
Include '. /SinglePullMessage. class. php'; $ object = new SinglePullMessage ('2017. 168.95.11 '); # send a message $ sender = 'boss'; # sender $ to = 'jar'; # receiver $ message = 'How are you '; # Information $ time = time (); $ arr = array ('sender' => $ sender, 'message' => $ message, 'time' => $ time ); echo $ object-> sendSingle ($ to, $ arr );
2. Get new messages
# Create test2.php
Include '. /SinglePullMessage. class. php'; $ object = new SinglePullMessage ('2017. 168.95.11 '); # Get the new message $ arr = $ object-> getNewMessage ('Jane'); if ($ arr) {echo $ arr ['Count']. "New message sent from contacts <br/>
Access results:
3. Get old messages
# Create test3.php
Include '. /SinglePullMessage. class. php'; $ object = new SinglePullMessage ('2017. 168.95.11 '); # Get the old message $ arr = $ object-> getPreMessage ('jar'); if ($ arr) {$ object-> dealArr ($ arr );} else echo "no old data ";
4. Multiple-to-multiple message transmission
Example 2: Send and retrieve multiple-to-multiple messages (Group)
Module requirements:
1. You can create a group and become the group owner.
2. The group owner can pull in as a group member and kicker
3. Users can exit the group directly.
4. messages can be sent. Each member can pull messages.
5. The maximum message capacity of a group is 5000.
6. Members can pull new messages and prompt how many new messages are there.
7. Members can retrieve the previously read old messages by page.
..... You can write these functions. Students who need or want to practice can add other functions, such as ban, anonymous message sending, and file sending.
Implementation of Redis:
1. Group messages and group members are stored in an ordered set. Member in an ordered set of Group messages stores the json data messages sent by users. score stores the unique value. The atomic operation incr is used to obtain the auto-increment value in the string for storage; the member of the group members in an ordered set stores the user, and the score stores non-zero numbers (here, the score is of little significance. In my example code, the number 1 is the score of the group master, and the other is 2. Of course, this data can be used to expand other functions, such as the membership level in the group.) See the following data storage structure diagram.
2. The group to which a user joins is stored in an ordered set. Among them, the member storage group ID and score storage users have obtained the maximum message score of the group (corresponding to the score value of the group message)
3. When creating a group, you can use the atomic operation incr to obtain a unique ID.
4. When a user sends a message in a group, the user obtains a unique self-increasing ordered ID through the atomic operation incr.
5. When executing incr, to prevent competition caused by concurrency, You need to perform the lock operation. [For details about redis lock, refer to: Redis build distributed lock http://www.bkjia.com/article/424704.htm]
6. A brief idea for creating a group. Any user can create a group chat. When creating a group chat, you can select whether to add group members (the parameters are in the form of arrays ). The creation process creates an ordered set of group members for this group (the ordered set of group information is not created at the moment), and then adds the group owner, then, add the group ID to the sorted set of groups that the user participates in.
Data storage structure:
PHP code implementation:
# ManyPullMessage. class. php
<? Phpclass ManyPullMessage {private $ redis = ''; # store redis object/*** @ 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 is used to create a group. When creating a group, users can also be pulled into the group. ** @ param $ user string | user name, create the group owner * @ param $ addUser array | array composed of other users ** @ param $ lockName string | lock name, used to obtain the group ID Use * @ return int to return the group ID */public function createGroupChat ($ user, $ addUser = array (), $ lockName = 'chatidlock ') {$ identifier = $ this-> getLock ($ lockName); # obtain the lock if ($ identifier) {$ id = $ this-> redis-> incr ('groupchatid '); # Get group ID $ this-> releaseLock ($ lockName, $ identifier); # Release lock} else return false; $ messageCount = $ this-> redis-> set ('countmessage _'. $ id, 0); # initialize the message counter of this group # enable the non-transactional pipeline and send all redis commands to redis at a time to reduce the number Connection $ pipe = $ this-> redis-> pipeline (); $ this-> redis-> zadd ('groupchat _'. $ id, 1, $ user); # create an ordered set of group members, and add the group owner # Add this group to the user's group ordered collection $ this-> redis-> zadd ('hasgroupchat _'. $ user, 0, $ id); foreach ($ addUser as $ v) # User Members to be added when creating a group {$ this-> redis-> zadd ('groupchat _'. $ id, 2, $ v); $ this-> redis-> zadd ('hasgroupchat _'. $ v, 0, $ id) ;}$ pipe-> exec (); return $ id; # Return group ID}/*** @ desc the group owner actively pulls people into the group ** @ param $ user string | Group main name * @ param $ groupChatID int | group ID * @ param $ addMembers array | users who need to be pulled into the group ** @ return bool */public function addMembers ($ user, $ groupChatID, $ addMembers = array () {$ groupMasterScore = $ this-> redis-> zscore ('groupchat _'. $ groupChatID, $ user); # obtain the groupChatName group owner if ($ groupMasterScore = 1) # determine whether the user is a group master {$ pipe = $ this-> redis-> pipeline (); # enable the non-transaction pipeline foreach ($ addMembers as $ v) {$ this-> redis-> zadd ('grou PChat _'. $ groupChatID, 2, $ v); # Add to group $ this-> redis-> zadd ('hasgroupchat _'. $ v, 0, $ groupChatID); # Add group names to the user's ordered collection} $ pipe-> exec (); return true;} return false ;} /*** @ desc ** @ param $ user string | group name * @ param $ groupChatID int | group ID * @ param $ delMembers array | Name of the member to be deleted ** @ return bool */public function delMembers ($ user, $ groupChatID, $ delMembers = array () {$ groupMasterScore = $ this-> redis-> Zscore ('groupchat _'. $ groupChatID, $ user); if ($ groupMasterScore = 1) # judge whether the user is a group master {$ pipe = $ this-> redis-> pipeline (); # enable non-transaction 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 func Tion quitGroupChat ($ user, $ groupChatID) {$ this-> redis-> zrem ('groupchat _'. $ groupChatID, $ user); $ this-> redis-> zrem ('hasgroupchat _'. $ user, $ groupChatID); return true ;} /*** @ desc send message ** @ param $ user string | username * @ param $ groupChatID int | group ID * @ param $ messageArr array | contains the array of sent messages *@ param $ preLockName string | group message lock prefix, group message lock Full name: countLock _ group ID ** @ return bool */public function sendMessage ($ user, $ GroupChatID, $ messageArr, $ preLockName = 'countlock _ ') {$ memberScore = $ this-> redis-> zscore ('groupchat _'. $ groupChatID, $ user); # member score if ($ memberScore) {$ identifier = $ this-> getLock ($ preLockName. $ groupChatID); # obtain the lock if ($ identifier) # determine whether the lock is successfully obtained {$ messageCount = $ this-> redis-> incr ('countmessage _'. $ groupChatID); $ this-> releaseLock ($ preLockName. $ groupChatID, $ identifier); # Release lock} else return false; $ json_me Ssage = json_encode ($ messageArr); $ this-> redis-> zadd ('groupchatmessage _'. $ groupChatID, $ messageCount, $ json_message); $ count = $ this-> redis-> zcard ('groupchatmessage _'. $ groupChatID); # view the amount of information if ($ count> 5000) # Check whether the amount of data has reached 5000 {# The amount of data exceeds 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 ** @ pa Ram $ user string | username ** @ return: return the json data array. If no new information is available, false */public function getNewMessage ($ user) is returned) {$ arrID = $ this-> redis-> zrange ('hasgroupchat _'. $ user, 0,-1, 'withscores'); # obtain the user's group ID $ json_message = array (); # initialize foreach ($ arrID as $ k => $ v) # traverse all groups in the loop, check whether a new message {$ messageCount = $ this-> redis-> get ('countmessage _'. $ k); # maximum group information score if ($ messageCount> $ v) # determine whether the user has unread new messages {$ json_message [$ k] ['message'] = $ t His-> redis-> zrangebyscore ('groupchatmessage _'. $ k, $ v + 1, $ messageCount); $ json_message [$ k] ['Count'] = count ($ json_message [$ k] ['message']); # count the number of new messages $ this-> redis-> zadd ('hasgroupchat _'. $ user, $ messageCount, $ k); # update the acquired message} if ($ json_message) return $ json_message; return false ;} /*** @ desc obtain group information by page ** @ param $ user string | user name * @ param $ groupChatID int | group ID * @ param $ page int | page *@ param $ size Int | how many pieces of data on each page ** @ return: json data is returned successfully; false is returned if a result is returned */public function getPartMessage ($ user, $ groupChatID, $ page = 1, $ size = 10) {$ start = $ page * $ size-$ size; # start data capture position $ stop = $ page * $ size-1; # end the data truncation position $ json_message = $ this-> redis-> zrevrange ('groupchatmessage _'. $ groupChatID, $ start, $ stop); if ($ json_message) return $ json_message; return false ;} /*** @ desc lock method ** @ param $ lockName string | lock name * @ param $ time Out int | lock expiration time ** @ return returns identifier/returns false if the lock is successful */public function getLock ($ lockName, $ timeout = 2) {$ identifier = uniqid (); # obtain the unique identifier $ timeout = ceil ($ timeout); # Make sure it is an integer $ end = time () + $ timeout; while (time () <$ end) # loop lock acquisition {/* # The set operation here can be equivalent to the following if operation, in addition, if ($ this-> redis-> set ($ lockName, $ identifier array ('nx ', 'ex' => $ timeout) can be reduced once for communication with redis ))) return $ identifier; */if ($ this-> redis-> setnx ($ lockName, $ identi Fier) # Check whether $ lockName is locked {$ this-> redis-> expire ($ lockName, $ timeout); # Set the expiration time for $ lockName return $ identifier; # returns a one-dimensional identifier} elseif ($ this-> redis-> ttl ($ lockName) ===- 1) {$ this-> redis-> expire ($ lockName, $ timeout); # Check whether an expiration time is set. If no expiration time is set, add} usleep (0.001); # Stop 0.001 ms} return false ;} /*** @ desc release lock ** @ param $ lockName string | lock name * @ param $ identifier string | unique lock value ** @ param bool */public function releaseL Ock ($ lockName, $ identifier) {if ($ this-> redis-> get ($ lockName) = $ identifier) # Check whether the lock has been modified by other clients {$ this-> redis-> multi (); $ this-> redis-> del ($ lockName ); # Release lock $ this-> redis-> exec (); return true;} else {return false; # other clients have modified the lock and cannot delete others' locks. }}?>
Test:
1. Create createGroupChat. php (test the Group Creation function)
Run the code and create groups 568 and 569 (the Group master is jack)
Include '. /ManyPullMessage. class. php '; $ object = new ManyPullMessage ('2017. 168.95.11 '); # create a group $ user = 'jack'; $ arr = array ('jan1', 'jane2'); $ a = $ object-> createGroupChat ($ user, $ arr); echo "<pre>"; print_r ($ a); echo "</pre>"; die;
2. Create addMembers. php (test the member adding 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>";die;
3. Create delete. php (test the group master's function of deleting members)
Include '. /ManyPullMessage. class. php '; $ object = new ManyPullMessage ('2017. 168.95.11 '); # the group owner deletes the member $ c = $ object-> delMembers ('jack', '123456', array ('jan1', 'jane4 ')); echo "<pre>"; print_r ($ c); echo "</pre>"; die;
4. Create sendMessage. php (test the message sending function)
Execute multiple times, send several messages in 568 and 569
Include '. /ManyPullMessage. class. php '; $ object = new ManyPullMessage ('2017. 168.95.11 '); # send a message $ user = 'jane2'; $ message = 'go go'; $ groupChatID = 568; $ arr = array ('sender' => $ user, 'message' => $ message, 'time' => time ()); $ d = $ object-> sendMessage ($ user, $ groupChatID, $ arr); echo "<pre>"; print_r ($ d); echo "</pre> "; die;
5. Create getNewMessage. php (test the user's function of getting new messages)
Include '. /ManyPullMessage. class. php '; $ object = new ManyPullMessage ('2017. 168.95.11 '); # The user obtains the new message $ e = $ object-> getNewMessage ('jane2'); echo "<pre>"; print_r ($ e ); echo "</pre>"; die;
6. Create getPartMessage. php (test the user's ability to obtain partial messages from a group)
(Send more messages for testing. 568 18 pieces of data)
Include '. /ManyPullMessage. class. php '; $ object = new ManyPullMessage ('2017. 168.95.11 '); # obtain partial messages of a group $ f = $ object-> getPartMessage ('jane2', 568, 1, 10); echo "<pre> "; print_r ($ f); echo "</pre>"; die;
Page = 1, size = 10
Page = 2, size = 10
After the test is completed, you also need other functions to modify and add the test on your own.
This article is relatively fast, and I have been thinking about finishing the article and learning other technologies. Haha. Please stay tuned. Please give me some advice on redis. My career direction is PHP.
The above is all the content of this article. I hope this article will help you in your study or work. I also hope to provide more support to the customer's home!