Redis to realize multi-people chat room function _redis

Source: Internet
Author: User
Tags redis

This article shares the design code for Redis to support the multi-person multi-chat room function for your reference. The specific content is as follows

Design principles


A data field on the left represents two chat rooms, the chat room id is 827,729

In the chat room 827, there are 2 people, jason22 and jeff24. They have read the messages with id 5 and 6 in the chat room respectively

A data field on the right represents that the user is in a different chat room. Jason22 participated in the 827 and 729 chat rooms. In these two chat rooms, he read the messages with id 5 and id 10 respectively.

In addition, the message with id 5 in the 827 chat room is different from the message with id 5 in the 729 chat room.

There are also three domains
msgs: chatid
This is a zset, ordered set, member is the message body, and score is the message id
Represents a message that has been sent out in a chat room
In addition, there are useful messages stored in it, and messages that have been read by everyone will be deleted

ids: chatid
It is a String type data, which contains the number of the latest message (when sending a message, increment this field to get the latest value)

ids: chat:
It is a String type data, which contains the latest chat room number (this field is incremented when creating a chat room)

Code

OK to start looking at the code
public String createChat (Jedis conn, String sender, Set <String> recipients, String message) {
// When starting, there is no ids in redis: chat: this key
// Return 1 after increment
    String chatId = String.valueOf (conn.incr ("ids: chat:"));
    return createChat (conn, sender, recipients, message, chatId);
  }
 
 
  / **
  *
  * @param conn
  * @param sender The person who sent the message
  * @param recipients
  * @param message The message to be sent
  * @param chatId chat room number
  * @return
  * /
  public String createChat (Jedis conn, String sender,
    Set <String> recipients, String message, String chatId) {
  // You can receive the message yourself
    recipients.add (sender);
 
 
    Transaction trans = conn.multi ();
    for (String recipient: recipients) {
  // The members of the chat room all read the 0th message at the beginning
      trans.zadd ("chat:" + chatId, 0, recipient);
  // Record the chat room everyone attended
      trans.zadd ("seen:" + recipient, 0, chatId);
    }
    trans.exec ();
 
 
    return sendMessage (conn, chatId, sender, message);
  }
 
 
  public String sendMessage (Jedis conn, String chatId, String sender, String message) {
   
  // Lock the chat room. Why? The staff changed.
  // This acquireLock see the previous chapter
    String identifier = acquireLock (conn, "chat:" + chatId);
    if (identifier == null) {
      throw new RuntimeException ("Couldn't get the lock");
    }
    try {
    // Set an up-to-date number for the message to be published The first time it returns 1
      long messageId = conn.incr ("ids:" + chatId);
      HashMap <String, Object> values = new HashMap <String, Object> ();
      values.put ("id", messageId);
      values.put ("ts", System.currentTimeMillis ());
      values.put ("sender", sender);
      values.put ("message", message);
      String packed = new Gson (). ToJson (values);
       
      // Message list of a chat room
      // The oldest message-message json
      // The default zset is sorted according to the score value from small to large
      conn.zadd ("msgs:" + chatId, messageId, packed);
    } finally {
      releaseLock (conn, "chat:" + chatId, identifier);
    }
    return chatId;
  }
 The message is now OK, and the rest is for the user to pull unread messages. This is troublesome, well, quite troublesome
 @SuppressWarnings ("unchecked")
  public List <ChatMessages> fetchPendingMessages (Jedis conn, String recipient) {
   
  // Get the id of the latest message the user has seen in each chat room
  // There are several chat rooms. The size of the seenSet is just a few
    Set <Tuple> seenSet = conn.zrangeWithScores ("seen:" + recipient, 0, -1);
    List <Tuple> seenList = new ArrayList <Tuple> (seenSet);
 
 
    Transaction trans = conn.multi ();
    for (Tuple tuple: seenList) {
      String chatId = tuple.getElement ();
      int seenId = (int) tuple.getScore ();
      // Get all unread messages in each chat room
      // min and max can be -inf and + inf
      trans.zrangeByScore ("msgs:" + chatId, String.valueOf (seenId + 1), "inf");
    }
    // I participated in several chat rooms. The length of the results is just a few
    List <Object> results = trans.exec ();
 
 
    //com.google.gson.Gson jar package download it yourself
    Gson gson = new Gson ();
    Iterator <Tuple> seenIterator = seenList.iterator ();
    Iterator <Object> resultsIterator = results.iterator ();
 
 
    // The last unread message successfully pulled by the user is stored in chatMessages
    List <ChatMessages> chatMessages = new ArrayList <ChatMessages> ();
    List <Object []> seenUpdates = new ArrayList <Object []> ();
    List <Object []> msgRemoves = new ArrayList <Object []> ();
     
     
    // This big while loop user loops several times in several chat rooms
    while (seenIterator.hasNext ()) {
      Tuple seen = seenIterator.next ();
      Set <String> messageStrings = (Set <String>) resultsIterator.next ();
      if (messageStrings.size () == 0) {
      // No unread messages
        continue;
      }
 
 
      // The code runs here
      // Description I have unread messages in a chat room
      // seedid records the message that I have pulled, the initial value is 0
      int seenId = 0;
      // Which chat room is currently being processed
      String chatId = seen.getElement ();
       
      List <Map <String, Object >> messages = new ArrayList <Map <String, Object >> ();
       
      // List of unread messages in the chat room
      for (String messageJson: messageStrings) {
        Map <String, Object> message = (Map <String, Object>) gson.fromJson (
          messageJson, new TypeToken <Map <String, Object >> () {}. getType ());
        int messageId = ((Double) message.get ("id")). intValue ();
 
 
        if (messageId> seenId) {
          seenId = messageId;
        }
        message.put ("id", messageId);
        // Add to the list of successful pull
        messages.add (message);
      }
      // Update the latest news I read in this chat room
      conn.zadd ("chat:" + chatId, seenId, recipient);
       
      // Record the latest record I read in a chat room
      seenUpdates.add (new Object [] {"seen:" + recipient, seenId, chatId});
 
 
      // Remove the 0th member-score
      Set <Tuple> minIdSet = conn.zrangeWithScores ("chat:" + chatId, 0, 0);
      // Why delete? Each chat room is a zset table The first record represents the message that all users have read at least
      if (minIdSet.size ()> 0) {
      Tuple tuple = minIdSet.iterator (). Next ();
      System.out.println ("tuple to delete:" + tuple.getElemen
t () + "-" + tuple.getScore ());
        msgRemoves.add (new Object [] {"msgs:" + chatId, tuple.getScore ()});
      }
      chatMessages.add (new ChatMessages (chatId, messages));
    }
 
 
    trans = conn.multi ();
    for (Object [] seenUpdate: seenUpdates) {
      trans.zadd (
        (String) seenUpdate [0],
        (Integer) seenUpdate [1],
        (String) seenUpdate [2]);
    }
    for (Object [] msgRemove: msgRemoves) {
      trans.zremrangeByScore (
        (String) msgRemove [0], 0, ((Double) msgRemove [1]). IntValue ());
    }
    trans.exec ();
 
 
    // The return is the latest news I got from this pull
    return chatMessages;
  }
OK, let's look at the test code:

package redisinaction;
 
 
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
 
import org.junit.BeforeClass;
import org.junit.Test;
 
 
import jedis.redis_in_action.Chapter06;
import jedis.redis_in_action.Chapter06.ChatMessages;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
 
 
/ **
 * This class is used for ...
 * @author dlf (460795365@qq.com)
 * @version 1.0, October 17, 2016 at 10:15:58 PM
 * /
public class Chapter06Test {
  static Jedis conn = null;
  static Chapter06 c = null;
   
  @BeforeClass
  public static void initConn () {
    System.out.println ("test before");
    conn = new Jedis ("10.150.0.80");
    conn.auth ("dlf123123");
     
    c = new Chapter06 ();
  }
   
 
 
   
  @Test
   public void testMultiRecipientMessaging () {
      System.out.println ("\ n ----- testMultiRecipientMessaging -----");
      conn.del ("ids: chat:", "msgs: 1", "ids: 1", "seen: joe", "seen: jeff", "seen: jenny");
 
 
      System.out.println ("Let's create a new chat session with some recipients ...");
      Set <String> recipients = new HashSet <String> ();
      recipients.add ("jeff");
      recipients.add ("jenny");
      String chatId = c.createChat (conn, "joe", recipients, "message 1");
      System.out.println ("Now let's send a few messages ...");
      for (int i = 2; i <5; i ++) {
        c.sendMessage (conn, chatId, "joe", "message" + i);
      }
      System.out.println ();
      System.out.println ("Look at the message library");
      // All messages in the message library
      Set <Tuple> messageFromBase = conn.zrangeWithScores ("msgs:" + chatId, 0, -1);
      Iterator <Tuple> iterator = messageFromBase.iterator ();
      while (iterator.hasNext ()) {
        Tuple tuple = iterator.next ();
        System.out.println (tuple.getElement () + "--" + tuple.getScore ());
      }
      System.out.println ("And let's get the messages that are waiting for jeff and jenny ...");
       
       
      List <ChatMessages> r1 = c.fetchPendingMessages (conn, "jeff");
      List <ChatMessages> r2 = c.fetchPendingMessages (conn, "jenny");
    // When I pull joe's unread message, the message in msgs: 1 will be deleted
    // Why? Want to understand?
      List <ChatMessages> r3 = c.fetchPendingMessages (conn, "joe");
      System.out.println ("They are the same?" + R1.equals (r2));
       
      System.out.println ("Those messages are:");
      for (ChatMessages chat: r1) {
        System.out.println ("chatId:" + chat.chatId);
        System.out.println ("messages:");
        for (Map <String, Object> message: chat.messages) {
          System.out.println ("" + message);
        }
      }
 
 
      System.out.println ("See if there are any");
      messageFromBase = conn.zrangeWithScores ("msgs:" + chatId, 0, -1);
       iterator = messageFromBase.iterator ();
      while (iterator.hasNext ()) {
        Tuple tuple = iterator.next ();
        System.out.println (tuple.getElement () + "--" + tuple.getScore ());
      }
      conn.del ("ids: chat:", "msgs: 1", "ids: 1", "seen: joe", "seen: jeff", "seen: jenny");
    }
   
}

It's done, everyone may copy the code and see for yourself
The following is the test result

test before

----- testMultiRecipientMessaging -----
Let's create a new chat session with some recipients ...
Now let's send a few messages ...

Look at the message library
{"sender": "joe", "id": 1, "message": "message 1", "ts": 1477276890018} -- 1.0
{"sender": "joe", "id": 2, "message": "message 2", "ts": 1477276890113} -- 2.0
{"sender": "joe", "id": 3, "message": "message 3", "ts": 1477276890115} -- 3.0
{"sender": "joe", "id": 4, "message": "message 4", "ts": 1477276890116} -- 4.0
And let's get the messages that are waiting for jeff and jenny ...
Tuple to delete: jenny--0.0
Tuple to delete: joe--0.0
Tuple to delete: jeff--4.0
They are the same? True
Those messages are:
  chatId: 1
    messages:
      {sender = joe, id = 1, message = message 1, ts = 1.477276890018E12}
      {sender = joe, id = 2, message = message 2, ts = 1.477276890113E12}
      {sender = joe, id = 3, message = message 3, ts = 1.477276890115E12}
      {sender = joe, id = 4, message = message 4, ts = 1.477276890116E12}
See if there are

The above is the entire content of this article. I hope it will be helpful for everyone's learning and I hope you can support the Yunqi community.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.