共计 1766 个字符,预计需要花费 5 分钟才能阅读完成。
im 即时通讯开发:群聊音讯是即扩散读还是即扩散写?
最根本的计划:“在线的群友不存储音讯,离线的群友才存储”
群信息,用户信息,群成员关系都是根底数据:
group_info(gid, group_info);
user_info(uid, user_info);
group_members(gid, uid);
假如一个群 (gid) 里有 4 个成员,其中三个在线(A, uid1, uid2),一个不在线(uid3)。
A 发送了一条音讯,很容易想到,对于不同的群友音讯存多份,每个群友一个队列来存储。但因为在线的用户会实时的收到音讯,所以暂定只为离线的用户存储。
用户收到的群音讯,也是根底数据:
user_msgs(uid,msgid,gid,sender_uid,time,content);
1)发送音讯;
2)查问状态;
3)不在线的存储离线;
4)在线的实时推送。
“在线的群友不存储,离线的群友才存储”会带来的问题是,如果第四步产生异样,群友会失落音讯。
优化的计划:“不论群员是否在线,都要先存储音讯”
音讯的可达性是聊天零碎中最重要的因素(没有之一),故这个计划是不行的,须要优化为“不论是否在线,都要先存储”。
发送群音讯的流程优化为,
1)发送音讯;
2)所有人都存一份;
3)查问状态;
4)在线的实时推送。
先将音讯落地,可能保障音讯可达性,那何时能力删除曾经落地的群音讯呢?咱们持续往下看。
对于在线的群友:收到群音讯后,给个 ack 确认能力删除。
画外音:逻辑删除,还是物理删除,依据业务是否有音讯漫游决定。
对于离线的群友:在下次登陆后,拉取完离线音讯再给 ack 确认能力删除。
总之:为了保障音讯的可达性,不论是在线音讯还是离线音讯,必须接管方给 ack 确认,能力删除音讯。
不论群员是否在线,都冗余一份群音讯”带来的问题
“不论是否在线,都冗余一份群音讯”带来的问题是:同一条音讯存储了很屡次,对磁盘和带宽造成了很大的节约。
很容易想到的优化是:群音讯实体存储一份,用户只冗余音讯 ID。即时通讯聊天软件开发能够征询蔚可云。
故根底数据能够由:
user_msgs(uid,msgid,gid,sender_uid,time,content);
优化为:
group_msgs(msgid,gid,sender_uid,time,content);
user_msgs(uid, msgid, gid);
这个优化,对于音讯投递,以及音讯删除的外围流程没有影响,几个实际为:
在线用户投递音讯实体,ack 音讯 ID;
离线用户先拉取音讯 ID,再拉取音讯实体,再 ack 音讯 ID。
如此这般,如果在某个群友 A 期间,群里陆续发送了 N 条音讯,则 user_msgs(uid, msgid, gid)里,会有 uidA -> mid1,mid2, mid3, … midN 等 N 条离线记录,拉取离线音讯时,能够把这 N 条音讯一次性拉取出来,而后再删除:
delete from user_msgs where msgid in($mid1,$mid2…, $midN) and gid=$gid
终级计划:利用群音讯的“偏序”个性优雅地实现“只存 1 份”
然而,群音讯具备“偏序”个性,下面的一次性删除齐全能够优化为:
delete from user_msgs
where msgid >= $mid1 and gid=$gid
这就意味着,每个用户只须要记录“最近一次收到的音讯 ID”,而不必记录“所有未收到的音讯 ID 汇合”,每当收在线音讯 ack,以及拉离线音讯 ack 时,只须要更新这个“最近一次收到的音讯 ID”即可。
于是乎,根底数据能够由:
group_members(gid, uid);
group_msgs(msgid,gid,sender_uid,time,content);
user_msgs(uid, msgid, gid);
优化为:
group_members(gid, uid, last_ack_msgid);
group_msgs(msgid,gid,sender_uid,time,content);
user_msgs(uid, msgid, gid); // 不再须要
即:群音讯只存储一份,群友无需冗余任何音讯实体,或者音讯 ID 了。
对于在线的群友:收到群音讯后,批改这个 last_ack_msgid。
对于离线的群友:拉取群音讯后,也批改这个 last_ack_msgid。