消息拉取
两个或多个客户端在互相发送和接受消息的时候,通常会使用以下两种方法来传递消息 。第一种方法被称为消息推送(push messaging),也就是由发送者来确保所有接收者已经成功接受到了消息。Redis内置了用于进行消息推送的PUBLISH命令和SUBSCRIBE命令。以前我们说过了这两个命令的缺陷。
第二种方法被称为消息拉取(pull messaging),这种方法要求接收者自己去获取存储在某种邮箱(mailbox)里面的消息。
尽管消息推送非常 有用,但是当客户端因为某些原因而没办法一直保持在线的时候,采用这一消息传递方法的程序就会出现各种各样的问题。为了解决这个问题,我们将编写两个不同的消息拉取方法,并使用它们来代替PUBLISH命令和SUBSCRIBE命令。
3.获取消息
为了获取用户的所有未读消息,程序需要对记录用户数据的有序集合执行zrange命令,以此来获取群组ID以及已读消息ID,然后根据这两个ID,对用户参与的所有群组的消息有序集合执行zrangebyscore命令,以此来取得用户在各个群组中的未读消息。在取得聊天消息之后,程序将根据消息ID对已读有序集合以及群组有序集合里面的用户记录进行更新。最后,程序会查找并清楚那些已经被所有人收到的消息。
两个或多个客户端在互相发送和接受消息的时候,通常会使用以下两种方法来传递消息 。第一种方法被称为消息推送(push messaging),也就是由发送者来确保所有接收者已经成功接受到了消息。Redis内置了用于进行消息推送的PUBLISH命令和SUBSCRIBE命令。以前我们说过了这两个命令的缺陷。
第二种方法被称为消息拉取(pull messaging),这种方法要求接收者自己去获取存储在某种邮箱(mailbox)里面的消息。
尽管消息推送非常 有用,但是当客户端因为某些原因而没办法一直保持在线的时候,采用这一消息传递方法的程序就会出现各种各样的问题。为了解决这个问题,我们将编写两个不同的消息拉取方法,并使用它们来代替PUBLISH命令和SUBSCRIBE命令。
Redis的其中一种常见用法,就是让不同种类的客户端(如服务器进程、聊天室用户等)去监听或者等待他们各自独有的频道,并作为频道消息的唯一接收者,从频道那里接收传来的消息。很多程序员都选择了使用Redis的PUBLISH命令和SUBSCRIBE命令来发送和等待消息,但是当我们需要在遇到连接故障的情况下仍然不丢失任何消息的时候,PUBLISH命令和SUBSCRIBE命令就派不上用场了。
/**
* 创建群组聊天会话
*/
private String createChat(Jedis jedis, String sender, Set<String> recipients, String message) {
long chatId = jedis.incr("ids:chat:");
recipients.add("sender");
Transaction trans = jedis.multi();
for (String recipientsd : recipients) {
//将所有参与群聊的用户添加到有序集合里面
trans.zadd("chat:" + chatId, 0, recipientsd);
//初始化已读有序集合。
trans.zadd("seen:" + recipientsd, 0, chatId + "");
}
trans.exec();
//发送消息
return sendMessage(jedis, chatId, sender, message);
}
/**
* 发送消息,
* 我们将发送消息添加到群组有序集合里面,虽然消息发送包含了竞争条件,但只要使用前面说过的锁就可以很容易地解决。
*/
private long sendMessage(Jedis jedis, long chatId, String sender, String message) {
//添加锁,前面有说过
String identifier = acquireLock(jedis, "chat:" + chatId, 10000);
if (identifier == null) {
throw new RuntimeException("Couldn't get the lock");
}
try{
//x新消息Id+1
long mid = jedis.incr("ids:" + chatId);
long ts = System.currentTimeMillis();
Map<String,Object> packMap = Maps.newHashMap();
packMap.put("id", mid);
packMap.put("ts", ts);
packMap.put("sender", sender);
packMap.put("message", message);
String packed = JSONObject.toJSONString(packMap);
jedis.zadd("msgs:" + chatId, mid, packed);
return chatId;
}finally{
releaseLock(jedis, "chat:" + chatId, identifier);
}
}
3.获取消息
为了获取用户的所有未读消息,程序需要对记录用户数据的有序集合执行zrange命令,以此来获取群组ID以及已读消息ID,然后根据这两个ID,对用户参与的所有群组的消息有序集合执行zrangebyscore命令,以此来取得用户在各个群组中的未读消息。在取得聊天消息之后,程序将根据消息ID对已读有序集合以及群组有序集合里面的用户记录进行更新。最后,程序会查找并清楚那些已经被所有人收到的消息。