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.