微博怎样发布文章 微博和知乎中的 feed 流是如何实现的?
刚好最近想写一个Feed机制的文章,就来回答一下吧。
楼上各位其实把大体的情况都已经说的很明白了,我来分享一下我们目前线上一个feed实现机制,已经在生产环境运行了大半年。理论来说,百万级别用户没有什么问题。
为了节省大家流量,全程无图(其实是我懒得画 - - ),希望大家能把省下的流量钱来给我发红包,鼓掌 。
首先,抛去数据库这一块,数据库我想大家肯定知道怎么设计,但是查询肯定是个麻烦事, 所以我使用了redis进行一个冗余设计,开始介绍之前,需要了解什么是推拉模式,楼上说的两篇文章
新浪微博架构和FEED架构分析--人人架构新浪博客
,还有推拉模式以及时间分区拉模式的分析
微博feed系统的推(push)模式和拉(pull)模式和时间分区拉模式架构探讨
其实已经足够了解了,请各位看官如果未对推拉模式了解,那么请先看文章,再来看我的回答,我的回答只是阐述具体实现,谢谢。
那么我现在说一下redis这块怎么来完成推拉模式,以及内存尽可能节省,速度尽可能提高吧(当然,只是我理解的节省内存跟速度提高,如果看官们有其他的意见,我就两点要求,一、轻喷,二、说完再喷)。
1、实现
首先,先解决发布跟接收的问题,目前有大体以下几种方式:
1、推模式
什么是推模式?推模式就是,用户A关注了用户B,用户B每发送一个动态,后台遍历用户B的粉丝,往他们粉丝的feed里面推送一条动态。
2、拉模式
与推模式相反,拉模式则是,用户每次刷新feed第一页,都去遍历关注的人,把最新的动态拉取回来。
但是,不管推模式还是拉模式都存在若关注数量或者粉丝数量过多,导致遍历时间太长的问题,怎么去解决 ?这里就出现了第三种模式,推拉模式。
3、推拉模式
这是一种折中的解决方案,就是在线推,离线拉。粉丝几百上千万, 跟你发布动态同时在线的肯定也就只有那么顶天几百几千几万,何况这类大V很少,只推给在线的粉丝,离线的粉丝上线后,再去拉取动态即可!但是,不管是什么模式,每个用户都会维护一个类似发件箱跟收件箱的东西,保存自己发过的动态以及Feed动态(具体实现看下面),来完成推与拉。
而这里讲的,肯定就是推拉模式,用户A关注了用户B , 用户B发布动态则将动态推进用户A的feed,这里使用redis的zset实现,sort为time(记得以毫秒为时间戳,秒级在数据量达到一定程度后,会有读取不到的问题,比如以时间戳为分页页码),value为具体的动态 ID(为什么是动态ID, 其实很简单, 就是因为动态的内容可以进行缓存,在redis里面全部走ID,修改动态内容也需要修改一处,动态内容可以保存在hash结构里), 每个用户维护一个zset保存我发布的动态,一个zset保存我的feed动态,过期时间3~7天看情况而定。为什么要设计过期时间后面会细说。
OK,全局维护一个在线用户列表,怎么设计这个就自己琢磨了,为了防止用户挂后台导致与服务端为离线状态,所以最好是1~3小时未操作或者离线时间不大于3小时的,都当做在线处理,反正这个看情况定。
那么,当用户发了一条动态后,后台会有以下这些操作:
在线推: 异步遍历在线的粉丝,将动态ID,添加到粉丝的Feed中。
离线拉: 离线用户打开APP后,我们是会请求一个公共的入口接口,主做统计以及其他初始化操作,在这里,我们也开了一个异步线程,对用户进行Feed更新操作,防止用户进入APP后等待拉取时间过长,毕竟关注成千上万的人肯定有(其实万单位以下遍历都很快)。拉取过程其实就是把自己最后一条Feed的时间戳取出,去遍历关注的人的feed,将大于该时间的ID全部拉取回来。用户进入APP后,刷新即可看到最新操作。
另:如果有Feed新消息数提示的需求,可以在推拉的同时进行增加, 刷新feed时清空即可。
其实到这里,发布接收的问题已经解决了,那么有一个问题,用户feed里面过长,占用内存怎么办?
我是这么处理的,一个用户的feed第一次拉取的时候,feed长度为500条,在我们APP里,相当于50页,而后的数据,都走数据库。大页码翻页其实就是个伪需求而且耗性能的东西,用户除了第一次用这个APP,才会翻到底,第一次使用, 能有几个动态 ?而对于二次使用以上的用户,一般来讲, 翻了几页就已经到达上一次看过的地方了,所以500条数据,在关注量一般的情况下,内容已经足够消费,甚至达到疲劳,可能有关注量很大的用户他的Feed每天可能有很多很多动态,但是,不用说,肯定是做广告的,关注一堆人等着回粉,这种人更不会去消费内容,50页的内容,翻起来都累。当然,并不是说放弃了这些人,feed找不到走数据库嘛~~~~爱走不走,想走就给我翻50页再说~
还有一个问题,每个用户都维护自己的动态跟Feed队列,当用户上百万时,内存的占有量肯定不小,要怎么释放内存才合适 ?
这里就回到上面那个问题了,为什么要给feed的key设计过期时间?为什么是设计3~7天过期时间?
原因有以下:
一、一个用户3~7天不打开APP,可能已经对APP失去兴趣了,打开几率很小,或者已经被卸载了,没有存在的意义了。
二、3~7天未登陆APP,关注的人发的动态也不少了,Feed未拉取回来的数据肯定也不少,那么这时候去遍历其实拉取量很大,那么还不如直接全部重新拉一边或者拉取用户最后登陆时间后产出的数据。
到这里,其实已经差不多了,大部分业务逻辑已经足够满足,并且速度也理想,目前我们线上这种模式走了半年,feed一般都是10~80ms响应完毕。
好了,大概就是这样了。
最后说一句: