微博和知乎中的feed流是如何实现的?
简单来说,Feeds这块主要包括两块内容,就是⽣成feeds和更新feeds。
⽣成feeds是什么意思呢,⽐如我们已经关注的⼈做了特定操作,我们需要把这些活动加⼊你的feeds,让你接收到。
更新feeds包括的内容⽐较多,⼀种就是你关注点做了更新,⽐如你新关注了⼀个⼈,需要把他的活动加⼊已有feeds,与此类似,取消关注也⼀样;另⼀种就是你的关注点做了⼀些更新操作,⽐如你关注的⼀个⼈取消关注了⼀个问题。
我们先来谈feeds⽣成,⽤户A做了某操作,⽐如关注了⼀个问题,这时候我们⾸先到⽤户A的所有关注者,然后给需要推送的关注者推送此操作,⼤家可以把每个⼈的feeds简单想象为⼀个有序列表,推送很简单,就是在每个⼈的列表末尾添加这个操作。怎么样,是不是很简单。
然后我们谈feeds更新,第⼀种情况和第⼆种情况其实都是⼀样的,我们需要针对这些活动更新来更新feeds,⽐如我们新关注了⼀个⼈,这时候我们需要取出此⼈的活动历史,然后按照时间顺序把这些历史塞到你的feeds中。此操作的复杂度会⽐较⾼,需要使⽤合适的数据结构来达到最佳性能,⽬前是O(log(N))。
当然,真实情况并没有这么简单,还有很多其他逻辑和注意点,⽐如我们的feeds需要对多⼈做同样的操作做合并(⼤家可以⾃⾏把以上的feeds 有序列表变为有序集合),同样的内容的创建时间是要按最新操作的时间来计算的,⼀个⼈连续做很多操作需要对操作做合并等。所有⼤家看到的feeds相应的存储考虑到性能都会使⽤内存,除此之外所有的操作都需要做持久化存储,否则我们也就没法更新feeds了。
下⾯我们谈谈这⾥⾯的技术挑战和相关技术,此部分与知乎⽬前的技术决策和使⽤技术⽆关,我给⼤家分享⼏个国外团队的⼯程决策和他们的优化⼿段。
先来谈谈strava,他们使⽤了Kafka分布式【发布】【订阅】消息系统,这个系统⽤来事件(event)发布,还使⽤了Storm分布式实时计算平台,这个计算集会【订阅】Kafka的事件,然后完成相应的处理,在这块他们做了⼀个优化,就是⼀个事件不会推给所有关注者,只会推给活跃的⽤户(如何判定⼀个⽤户为活跃的,这就看实际情况和数据⾃⼰优化了)。
方舟编译器gitee然后再来谈谈Instagram,他们产品的读写⽐例达到了100:1,事实上⼤部分互联⽹产品都是这样,所以这也是推技术更合适的原因,推⼀次开销可能⼤⼀点,不过推(也就是写)发⽣次数⼤⼤少于读,因为有些⼤⽜关注者⾮常多,达到⼏百上千万,为了进⾏可靠地推,这个过程都会【异步】后台执⾏。同样,我们需要⼀个任务调度器和消息队列,任务调度他们选⽤了Celery,下⾯需要选择⼀个消
息队列,Redis依赖订阅者轮询,不⾃带复制备份,⽽且强依赖内存是个硬伤,不合适;Beanstalk其他⽅⾯不错,可还是不⽀持复制(Replication),弃掉;最后他们选⽤了RabbitMQ,快,⾼效,⽀持复制,⽽且和Celery⾼度兼容。
接着谈谈Pinterest,重⼼在于创建⼀个智能化的feed,也就是feed会包括⼀些热点和推荐,⽽且会根据特定算法来排序。当事件发⽣后,会经过⼀系列流程最后才进⼊⽤户的内容feed,⾸先经过智能feed worker来处理,这些worker会接收事件⽽且根据特定⽤户给事件打分,然后这些事件会被插⼊到⼀个排好序的feed池中,不同类型的事件会被插⼊各⾃的池中,⽬前他们使⽤HBase的基于key的排序实现了此优先队列,接着智能feed内容⽣成器接⼿了,它会从⼏个池中根据策略取出feeds甚⾄剔除⼀些feeds,最后⾯向⽤户的智能feed服务会整合旧的feed和新⽣成的feed,展现给⽤户看到的Home Feeds。
最后简单谈谈Facebook,⽤户量⼤了之后对⼯程团队要求会更⾼,每个facebook⽤户都会有⼀个属于⾃⼰的独⼀⽆⼆的动态⽣成的⾸页。很多团队都会⽤⽤户ID为Key来把feeds存⼊Key-Value存储系统,⽐如Redis,问题是通过⽹络连接做远程过程调⽤⾮常慢,⽆法满⾜实时性的要求,所以facebook团队也开始使⽤了嵌⼊式数据库,他们也开源了⾃⼰在⽤的RocksDB。
就像电影特效是为剧情服务⼀样,技术是为产品服务的。针对不同的业务场景,适合的技术也是不⼀
样的。随着产品的调整和业务规模的扩⼤,相应的技术都会做进化和调整。针对不同的难题,需要提出不同的技术⽅案,feeds的⽣成也是这样,如果有必要,我们也会对这些⽅案做调整,⽬的都是⼀样,那就是⼜对⼜快⼜稳定。
如果有什么错误,希望⼤神指出。如果有更好的⽅案或者建议,欢迎交流。
先不考虑数据的物理分布,仅讨论下业务的设计。
有三种基本思路:
【思路1】,产⽣的所有动态,都在同⼀条索引上,上⾯承载所有的更新,以及读取。每个⽤户都有⾃⼰的filter规则,这个规则包括「屏蔽了谁,关注了谁,屏蔽了哪个问题」。
⽤户在读取feeds,便是使⽤⾃⼰的filter,按照时间顺序,从头遍历这个索引,直到得到⾜够的条⽬。
优点:业务逻辑上是最清晰,性能稳定。
缺点:技术难度最⼤。
【思路2】,读扩散。(拉模式)
每个⼈有⾃⼰产⽣的feeds队列,打开⾃⼰的⾸页时,按照⾃⼰的关注列表和屏蔽规则,去读取其他⽤户的feeds队列,然后汇总、排序。
优点:实现最简单,关注列表 敏感性最⾼。
缺点:性能最差,⽽且差的很稳定。
【思路3】,写扩散。(推模式)
每个⼈有【⾃⼰的产⽣的feeds队列】和 【待读取的feeds队列】。每个⽤户产⽣动态时,压⼊到关注者的 待读取feeds队列,压⼊前,需要根据屏蔽规则来判断下是否要压⼊。
优点:每个⽤户页⾯打开的速度是最快的,性能最⾼。
18款app软件免费下载缺点:关注列表变化时,敏感度略低,但读取队列的时候,再根据规则过滤⼀遍,也没啥太⼤问题。
个⼈推荐:思路1 。
根据个⼈的简单猜测,知乎的⾸页,貌似是 思路3+思路2 。
猜测的理由:
1-当我取消关注时,刷新,页⾯还会有这个⽤户的动态,所以 思路3的可能性很⼤。
2-当我新关注⼀个⼈,刷新,页⾯上会有这个⽤户的动态,可能针对最近产⽣的关注⾏为做了特殊处理,少量的做了读扩散。
3-B关注了A。如果A账号产⽣⼀条动态,⽐如 赞同,然后再取消赞同。此时A账号的 全部动态 中,是没有刚才的赞同记录的,但是在 B 的⾸页上,还是会有 A 的赞同⾏为。
update at 2014-12-08
我今天偶然的发现,我关注的某些⼈的回答,是没有出现在我的⾸页的。
我反复对⽐过,估计 ⼤概是4天前的4、5条动态,没有出现在我的⾸页。
根据这个现象,知乎的⾸页应该是 写扩散更多些。
⽽且,在写扩散的时候,貌似写失败了,就拉倒了(1次性的)。
刚好最近想写⼀个Feed机制的⽂章,就来回答⼀下吧。
楼上各位其实把⼤体的情况都已经说的很明⽩了,我来分享⼀下我们⽬前线上⼀个feed实现机制,已
经在⽣产环境运⾏了⼤半年。理论来说,百万级别⽤户没有什么问题。
为了节省⼤家流量,全程⽆图(其实是我懒得画 - - ),希望⼤家能把省下的流量钱来给我发红包,⿎掌 。
⾸先,抛去数据库这⼀块,数据库我想⼤家肯定知道怎么设计,但是查询肯定是个⿇烦事, 所以我使⽤了redis进⾏⼀个冗余设计,开始介绍之前,需要了解什么是推拉模式,楼上说的两篇⽂章
新浪微博架构和FEED架构分析--⼈⼈架构paper0023新浪博客 ,
还有推拉模式以及时间分区拉模式的分析
微博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响应完毕。
好了,⼤概就是这样了。
这⾥看到⼀篇新浪微博做的技术分享,值得⼀看。虽然没有太多的技术实现细节,不过⼤的框架已经说明的差不多了。还是好⼏年前的分享,现在应该架构变化⼜⼤了很多,不过⼀个⼩⽹站参考的话,这些已经⾜够了。 新浪微博架构和FEED架构分析--⼈⼈架构paper0023新浪博客
另外⼀篇,⼀名⽹友对推拉模式以及时间分区拉模式的分析 微博feed系统的推(push)模式和拉(pull)模式和时间分区拉模式架构探讨
mysql面试题 知乎想象⼀下,每个⼈的feed就是⼀个list, ⼀般使⽤成熟的kv store来存储,可以是redis也可以casandra或者hbase或者mysql(当然要sharded)。⽤户前端产⽣的所有事件被统⼀发送到⼀个【event bus】,⽐如kafka或者各种message queue⾥⾯,然后应⽤层的server对这些事件进⾏监听,根据业务逻辑将新的item插⼊到下游⽤户的feed⾥⾯,虽说事实很复杂,但是落实下来就是⼀个prepend的操作⽽已。
基本都是这个思路,你可以把它叫做push模式,不过似乎采⽤pull的不多,所以push也就是业界主流了。其实没什么特别⽞乎的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论