使用 redis zset 保存 IM 聊天消息并判断消息可靠到达客户端

在服务端,用户收到的聊天消息,用一个 redis zset 保存,把聊天内容作为 member, 服务器时间戳当做 redis zset 的 score, 这样就可以对聊天记录进行排序

其他人向用户发送聊天消息时,服务端先把聊天消息保存到 redis zset 中,然后再下发给客户端
客户端每收到一个聊天消息,就把聊天消息的时间戳回复给服务器,这样服务器就能确认客户端读取了聊天消息
服务端要记录下每个用户回复的最后一条聊天时间戳, 把这个服务端变量设为 last_msg_timestamp
从服务端视角来说, 客户端上报的聊天消息的时间戳如果小于 last_msg_timestamp, 就不更新 last_msg_timestamp , 只有大于 last_msg_timestamp 时才更新 last_msg_timestamp 为客户端上报的聊天消息的时间戳


===============================================================================================


如果用户离线,收到的离线消息仍然保存在 redis zset 中
用户重新登录后,从 zset 中查询 [ last_msg_timestamp, +inf ],就可以获取到离线消息

但是服务端各个服务器的时间,误差小于 1 秒, 在纳秒级别上可能是有误差的
假设 a 连接在 A 服务器上, b 连接在 B 服务器上, c 连接在 C 服务器上, d 连接在 D 服务器上, A B C D 几台服务器的当前时间戳变量,设为 A_ts, B_ts, C_ts, D_ts, 且 C_ts < B_ts <= D_ts
 A_ts, B_ts, C_ts, D_ts 在秒级没有误差, 在纳秒级上有误差

b 向 a 发消息,经过的网络路径是 
b -> B( 把消息写入 redis zset, B_ts 作为 redis set score )  -> 消息队列 -> A -> a

c 向 a 发消息,经过的网络路径是 
c -> C( 把消息写入 redis zset, C_ts 作为 redis set score ) -> 消息队列 -> A -> a


===============================================================================================


从原理上来说,会出现这样一种情况:
b, c 先后向 a 发消息, a 先收到 b 的消息,把 last_msg_timestamp 设置为 B_ts 
而 C_ts 在纳秒级别上是小于 B_ts


引发的后果:
如果 a 收到 b 的消息后,向 A 回复了 last_msg_timestamp ,随即断线
那么下次登录,就获取不到 c 发来的离线消息了,产生消息丢失


解决办法:
由于 B, C 在秒级上是同步的,那么 a 获取离线消息的时候,对 redis set 查询的时间范围是 [ last_msg_timestamp 的 1 秒前, +inf], 就可以把 c 发来的离线消息读出来了
这样做的话,下发给客户端的消息可能会有重复,所以客户端要做去重处理


===============================================================================================

 

另外一种情况,用户登录后立即向 redis zset 获取离线消息,此时离线消息时间戳 > last_msg_timestamp, 如果刚获取到离线消息,紧接着有条新消息插入了 redis set, 恰好新消息的时间戳在纳秒级别上小于离线消息时间戳或者 last_msg_timestamp,
用户向服务端回复了离线聊天消息时间戳后,( last_msg_timestamp 的值更新为离线消息时间戳 ) 来不及接收新消息就下线,那么下次登录的时候,就读取不到这条新消息了

解决办法:
用户登录后,等待 1 秒钟再向服务器获取离线消息, 对 redis set 查询的时间范围是 [ last_msg_timestamp 的 1 秒前, +inf],就能把新消息也查询出来并下发给用户了

===============================================================================================

 

总结
经过上述推导,发现登录后等待 1 秒再拉取离线消息, 对 redis set 查询的时间范围是 [ last_msg_timestamp 的 1 秒前, +inf] 就不会产生消息丢失


 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值