如何设计app里已读、未读消息

一款app,消息页面有:钱包通知、最近访客等各种通知类别,每个类别可能有新的通知消息,实现已读、未读功能,包括多少个未读,这个是怎么实现的呢? 比如微博的:

除了常见的对话消息如“活动通知”,还有提示消息如“更新提示”,很多时候还得展示消息数量如“群推荐”。这些该如何去设计呢?

在做这类需求的时候,首先要做的是设计一个合适的业务模型,那么这个模型就是“及时消息模型(IM)”。

将问题中的"设置",“活动通知”,"更新提示","好友发布","最近来访"当做一个“虚拟人”来处理,你跟“虚拟人”组成了一个"对话列表(Msg_Group)"。“虚拟人”与正常人的区别就是,虚拟人与你的对话是单向的,只能他向你发消息,你无法回复。

比如“最近来访”标签,当有人访问你主页的时候,后端会以这个“最近来访”虚拟人的身份给你发一条消息,不过消息里还有一个特殊标记,标明了来源。我们除了要拉取总量,还有不同来源消息的数量。当然,一个动作不一定只发一条消息,比如,图中上方有个金刚键"消息",它是所有消息的总和,所以,投递其他消息的时候,也要给它投递一次,不过它只展示一个未读数字,所以这个消息只需要一个msg_id即可,不需要消息payload。

至于前端怎么展示,就看具体产品需求了,这里就不深入了。

每个对话可以看作一个Msg_Group,每条msg的msg_id都是有序递增的,至于msg_id只是队列内有序还是全局有序,就看你选择了,一般数据10亿以内没必要优化,发号器全局有序即可。每个消息有基本的信息:参与人(图中的例子只有2个,你和“虚拟人”),消息ID(msg_id),状态(state)。

所以,判断有没有小红点,或者小红点的数字是多少,就是简单的获取你与虚拟人的对话的未读的消息的数量。

但是消息的消费分为两种情况:

(1)连续性消费。已读是连续的,用户需要保存每个Msg_Group的last_read_id即可。

连续消费队列

(2)间断性消费。每个消息有个独立的标记位。但是因为不连续,所以需要遍历所有的消息才能统计出未读消息总量。为此需要设置一个水位线(WaterLine)。水位线表示线下的消息全部已读。在计数的时候,只遍历到水位线,不需要遍历所有的消息。

间断消费队列

消息列表的存储,读取,就比较多样了。可以是MySQL,nosql,hbase,redis。一般我们是混合存储,特别老的存hbase,比较老的存mysq或nosql,新数据存redis。云厂商也有专门针对这类场景的存储产品,比如阿里云的TableStore。大多数情况,我们只需要一个数量,固定从当前最大的msg_id往前取,如果取到100条还没完,直接返回99+完事了。

实际上,比如“设置”,"隐私设置",是整个产品全局的,所以可以弄个简单的"广播消息模式",广播模式就是维持一个单向的消息的队列,所有的人都可以拉取这个队列的消息,只需要他们各位维护自己的last_read_id即可。