Feed流系统 #
业务概述 #
角色模型
- 创作者(Followee):又称被关注者、Up、博主等。创作者可以发表信息(推文、博客)。
- 关注者(Follower):又称粉丝。关注者可以同时关注多个创作者。用户可以兼具创作者和关注者。
主要功能
- 创作者发布信息。
- 关注者关注/取关创作者。
- Feed流展示:用户主页展示关注的所有创作者的最新信息。Feed流可以是单纯基于时间的Timeline流,也可以是基于权重的Rank流,还可以是两者的结合。
数据建模 #
表结构
- Feed表:信息ID、创建人ID、创建时间、信息内容、信息状态。
- 信息ID包含创建时间(时间戳+递增序号)时可以省去额外的按时间排序。
- 分区方案:
- 信息ID:用户发布的信息可能保存在多个节点上,查询时需要跨区。
- 用户ID:用户发布的信息保存在单个节点上,可能分布不均,存在热点问题。极端情况下一个节点可能无法容纳单个用户的数据。
缓存方案
- 用户关系缓存:key=用户ID,field=用户关注的创作者IDs,value=创作者信息。
- 最新信息缓存:key=用户ID,value=信息IDs。用于为活跃用户缓存过去五天发布的推文,
- 收件箱缓存:当消息发布采用写扩散时,用来保存在线用户的数据,加速信息的获取速度。
消息处理模式 #
- 拉模式(读扩散):创作者在发布信息时写入自己的发件箱,关注者展示Feed流时需要从所有关注的人那里获得新信息后进行聚合。
- 推模式(写扩散):创作者在发布信息时写入所有关注者的收件箱。关注者通过自己的收件箱构建Feed流。有多少关注者,消息就需要保存多份,推送多次,所以写扩散时需要使用队列进行削峰。
- 混合模式:
- 按创作者的关注用户数区别处理,粉丝少采用写扩散,粉丝多则采用读扩散。
- 按用户是否在线区别处理,在线用户采用写扩散,离线用户采用读扩散。
生成Timeline型的Feed流 #
业务需求
- 显示当前用户关注的所有创作者的最新信息,按时间线倒序排列。
- 用户可以将关注的人进行分组,每个分组都有单独的Timeline。
- 当一个创作者删除某条信息后,关注他的用户应该看不到这条信息。
- 当一个用户取消关注某个创作者后,该用户就应该看不到该创作者的所有信息。
拉模式
- 创作者发布信息时写入收件箱和最新信息缓存。
- 从用户关系缓存中获取当前用户关注的所有创作者的ID。
- 根据创作者ID在最新信息缓存中找出所有匹配的信息。
- 在应用层面对信息ID集合进行聚合。
- 过滤掉已被删除的信息以及取关的创作者所创建的信息。
推模式
- 创作者发布信息时写入消息队列,广播到关注者的收件箱。
- 用户登录后从收件箱中取出所有信息进行排序。
- 过滤掉已被删除的信息以及取关的创作者所创建的信息。
基于上线状态的推拉集合模式
- 创建者发布信息时写入收件箱、最新信息缓存、在线用户的收件箱缓存。
- 检查收件箱缓存,没有数据时再使用拉模式进行聚合。
未读信息数方案 #
- offset:用户只存储最后一条已读消息的offset。适合IM聊天等场景。
- bitmap映射:将用户ID映射到bitmap上,已读设为1,未读设为0。适合需要知道消息是否已读的场景,如系统通知等。
- 差集对比:适合只需显示未读信息数,且数据规模大的场景。
- 在缓存中记录关注的所有创作者的信息数,key=用户ID,hashKey=关注的创作者ID,value=count,value为信息数。
- 生成Feed流时,记录用户当前关注的所有创作者的信息数。
- 未读信息数 = 用户当前关注的所有人的信息数 - 快照中的信息数。
- 用户点击未读消息后,将用户当前关注的信息数刷新到快照中。