feed 流的实现

我们先来谈feeds生成,用户A做了某操作,比如关注了一个问题,这时候我们首先找到用户A的所有关注者,然后给需要推送的关注者推送此操作,大家可以把每个人的feeds简单想象为一个有序列表,推送很简单,就是在每个人的列表末尾添加这个操作。怎么样,是不是很简单,:)

然后我们谈feeds更新,第一种情况和第二种情况其实都是一样的,我们需要针对这些活动更新来更新feeds,比如我们新关注了一个人,这时候我们需要取出此人的活动历史,然后按照时间顺序把这些历史塞到你的feeds中。此操作的复杂度会比较高,需要使用合适的数据结构来达到最佳性能,目前是O(log(N))。

当然,真实情况并没有这么简单,还有很多其他逻辑和注意点,比如我们的feeds需要对多人做同样的操作做合并(大家可以自行把以上的feeds有序列表变为有序集合),同样的内容的创建时间是要按最新操作的时间来计算的,一个人连续做很多操作需要对操作做合并等。所有大家看到的feeds相应的存储考虑到性能都会使用内存,除此之外所有的操作都需要做持久化存储,否则我们也就没法更新feeds了:)

下面我们谈谈这里面的技术挑战和相关技术,此部分与知乎目前的技术决策和使用技术无关,我给大家分享几个国外团队的工程决策和他们的优化手段。

先来谈谈strava,他们使用了Kafka分布式发布订阅消息系统,这个系统用来事件(event)发布,还使用了Storm分布式实时计算平台,这个计算集群会订阅Kafka的事件,然后完成相应的处理,在这块他们做了一个优化,就是一个事件不会推给所有关注者,只会推给活跃的用户(如何判定一个用户为活跃的,这就看实际情况和数据自己优化了)。

然后再来谈谈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

这些内容都在他们的技术博客里面有提到,链接在这里:
Strava Engineering
How Instagram Feeds Work: Celery and RabbitMQ
Making Pinterest
facebook.com/notes/1015

就像电影特效是为剧情服务一样,技术是为产品服务的。针对不同的业务场景,适合的技术也是不一样的。随着产品的调整和业务规模的扩大,相应的技术都会做进化和调整。针对不同的难题,需要提出不同的技术方案,feeds的生成也是这样,如果有必要,我们也会对这些方案做调整,目的都是一样,那就是又对又快又稳定。

如果有什么错误,希望大神指出。如果有更好的方案或者建议,欢迎交流。

 

發表迴響

你的電子郵件位址並不會被公開。