作者回复: 示例挺多的哈,给一个redis官网的例子:
local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],1)
end
作者回复: 思路是好的哈,不过很多时候不仅仅是用户登录的时候才需要总未读,比如每来一条消息需要进行系统推送时,由于苹果的APNs不支持角标未读的累加,只能每次获取总未读带下去。另外,客户端维护总未读这个也需要考虑比如离线消息太多,需要推拉结合获取时,到达客户端的消息数不一定是真正的未读消息数。
作者回复: 没关系哈,互相探讨的过程希望大家不要拘谨。正如你所说,redis在执行lua脚本过程中如果发生掉电,是可能会导致两个未读不一致的,因为lua脚本在redis中的执行只能保证多条命令会原子执行,整体执行完成才会同步给从库并写入aof,所以如果执行过程中掉电,会直接导致被中断的后面部分的脚本得不到执行。当然, 实际情况中这种概率非常小。作为兜底的方案,可以在未读变更时如果会话比较少,可以获取一次全量的会话未读来覆盖总未读,从而有机会能得到最终一致。
作者回复: 是的,对于需要使用lua的数据需要确保两个key能hash到一个节点。
作者回复: 1. 其实就是有一些纳入到总未读里的消息不一定会进行消息下推。
客户端统计总未读的情况如果是需要多终端同步或者离线消息下推采用推拉结合的,不一定会话会话就是全量的,这种情况计算总未读就会有误差。
2. 不需要这个前提,理论上只需要会话未读就可以保证准确,增加总未读是为了提升读取性能。
作者回复: 这个应该是客户端逻辑哈,点击未读数实际上是把最新的一条消息id带进去了,端上在已有的消息里查询这条消息就可以了。这也是为什么最近联系人需要带上最新的一条消息了。
作者回复: buffer缓冲和强一致性本身就是两个比较对立的概念,所以要做到既能缓冲请求频率又保证强一致性是比较困难的。如果可以的话,尽量让不容易宕机的一方来进行buffer缓冲,比如:如果是客户端和服务端都能缓冲,那还是让服务端来缓冲可能比较可靠一些。
作者回复: 对于点对点聊天,直接通过p2p的方式不经过服务端来收发消息是可行的,市面上较早就已经有类似的软件了。
作者回复: 未读数这个实际上访问量不大的话实现会灵活很多,上面这种实现实际上得看消息ID是否需要用到,不需要的话不用存储消息ID,否则对存储是一种浪费;另外会话未读的获取并发大的时候hgetall性能也是一个问题。具体看业务上是否够用哈
作者回复: 这个看场景吧,一般支持多终端消息同步的话建议采用推拉结合的方式,因为消息会在服务端存储,按会话维度拉取也比较方便。另外,第二个问题,一般点未读是直接展示最新的消息呀,不会跳到最旧的。
作者回复: 服务端接收到查看会话的请求时,除了返回会话内容,还会在服务端进行未读清理。另外课程中说的离线消息下推是指用户由断连再重新上线后的过程,不是指用户网络离线。所以收到离线消息并不会清未读。
作者回复: 跟着消息带下去,或者建连后从服务端同步一次然后端上通过接收的消息进行本地累加。
作者回复: 是的,redis的事务实际上需要使用方自行处理失败的后续操作。
作者回复: 是的,redis嵌入的lua脚本是会原子化执行的。具体使用可以参考redis官网示例。
作者回复: 对于群聊业务来说,由于消息扇出问题,一般处理上和点对点聊天不太一样。群聊消息一般采用”读扩散“的方式,一个群的消息只会记录一份,这个群里的每个用户在需要查看数据的时候都从会获取这个群的消息记录。未读数是每一个用户单独一份的,和消息不一样。
作者回复: 可以的,长连接入层和后面的业务层之间可以通过redis的pub sub来降低消息延迟,消息发送的api层和具体的持久化层出于成本考虑可以通过其他非内存型来实现,kafka由于是顺序的读写,写入和读取的性能有系统的PageCache来加速,所以性能上不会差。不知道你这里说的延迟批量发布消息具体是什么原因呢?
作者回复: 分布式锁需要能拿到锁就能保证同一时间只有拿到锁的进程才行执行操作,因为会话未读和总未读变更是在一个进程里,所以理论上是可以保证原子性的。但如果像你所说,第二条加未读的命令一直执行失败还是会出现不一致的情况,这种情况一个是重试,另外就是回滚第一个操作。
lua脚本这个可以考虑在脚本中增加一些修复机制,比如会话数比较少的情况下聚合一次未读来覆盖总未读。
作者回复: 是的,redis对于脚本执行并没有做到真正的事务性,lua脚本在redis中的执行只能保证多条命令会原子执行,整体执行完成才会同步给从库并写入aof,所以如果执行过程中掉电,会直接导致被中断的后面部分的脚本得不到执行。lua脚本中可以增加一些修复机制,比如会话比较少的话就聚合一次会话来覆盖总未读。
作者回复: 好像和提的问题不大对的上哈,redis对于脚本的执行问题在于并不能保证执行过程掉电后从库或者aof能够感知到来用于恢复。