好友动态的实现原理

kan18 8年前
   <p>今天准备跟大家聊的一个功能就是:好友动态功能。听起来好抽象,有木有 ~~ 那说直白一点:微信朋友圈。这下听懂了嘛?</p>    <p>我们现在好多软件都有一个类似朋友圈的功能:微博、微信、 QQ …… 只要涉及到好友、粉丝这样的 app 或者是网站,一定有这样的一个功能。那这个功能是怎么样来实现的呢?</p>    <p>其实,这个技术已经非常的老了…… 估计快 7-8 年了。那个时候有一个网站还非常流行,他叫:开心网。大家都在偷菜、抢车位,然后你的好友动态里就会出现“ xxx 又抢了车位”这样的信息。那会儿老王在贴吧,产品经理要求做一个叫做 i 贴吧的产品(不知道有没有人知道或者能记得),其中一个类似这样“ xxx 又发了一个贴子”的功能。同时代的,微博、微信等等纷纷拥有这些功能。这看起来就是一个烂大街的功能…… 后来到了百词斩,要求做一个好友动态的功能,看起来就完全是同一个功能的翻版。</p>    <p>那这个烂大街的功能是怎么样实现的呢?等老王慢慢道来。</p>    <h2><strong>1 、基础逻辑</strong></h2>    <p>如果你有 N 个好友,当他们其中有 K 个人发出了新的信息,你就可以看到按时间倒序的排列的动态。</p>    <p><img src="https://simg.open-open.com/show/7ce214599f77301e9d848d9a79f99c12.png"></p>    <p>有些时候,还会根据业务的要求,将某些动态信息进行合并,比如:某几个人都回复了同一个贴子,就可以合并为,“ xx1, xx2, xx3 都回复了 yyy 发的贴子《老王真帅》”。这样就使得动态消息看起来不是那么冗长繁杂。</p>    <p>好了,基本逻辑描述了这么多,想必大家都已经心领神会。几个必要的点:</p>    <p>A 、好友间的关注关系:单向关注一般称为粉丝;双向关注一般称为好友。为描述简单,统一称之为好友的关注关系;</p>    <p>B 、好友产生动态:好友做了需要记录和展示的动作;</p>    <p>C 、动态的聚合:多个好友产生的动态需要合并后展示。</p>    <h2><strong>2 、实现的原理</strong></h2>    <p>那具体怎么样实现呢?有几种方法,我们一一来介绍。</p>    <p>A 、 推 送到信箱。</p>    <p>这个是最容易想到的实现方法。假设我有一个信箱,如果我的好友,每次发新的动态的时候,都将他们的动态放一份到我的信箱里。这样,我每次去信箱里看的时候,就能从上到下看完所有的好友动态。</p>    <p><img src="https://simg.open-open.com/show/eb98755cb3a4a699ab9ff4541c81513b.png"></p>    <p>上图演示的就是有新动态的好友将新的动态推送到我的信箱。因为整个过程是信息产生者主动发送的过程,所以这种方式我们就叫做 推 (push) ,这是一种主动的行为。</p>    <p>优点:</p>    <p>那这种方式最明显的优点就是:</p>    <p>a 、逻辑简单。这个可以用非常简单的伪代码来表示。</p>    <p>foreach (Follower f in followerList)</p>    <p>{</p>    <p>f.mailbox.add(myMsg);</p>    <p>}</p>    <p>b 、新动态信息显示及时。在数据量不大的情况下,基本上可以做到实时或者是准实时。这边儿动态一发,那边就可以显示出来。</p>    <p>不过这个方法的问题也比较明显。比如,有一个明星,他有上千万的粉丝,如果他发一个动态…… 就只能呵呵了。我们简单算一下,如果每个动态推送给好友的时间假定为 1 个毫秒,那么 1000 万条动态顺序推送的话,就需要 1ms * 1000w = 1ws 约等于 3 个小时。</p>    <p>也就是说,我们的系统什么事情都不做,在保证效率足够高的情况下,将这条动态推送给这个明星的所有关注者,需要 3 个小时。也就是说,最后一个粉丝要动态发生后 3 个小时才能看到。这个时效明显就失去了动态这个产品的意义了。</p>    <p>那对于这个算法,我们有什么改进的方法嘛?</p>    <p>当然有(不然老王怎么往下讲呢,对不对)!既然串行(一条条的)推很慢,那我们就让推这个动作并行起来。因为推这个动作消耗主要在网络上,而对于推送的机器来讲, cpu 消耗是很低的,所以我们可以通过增加线程的方式,来提高推送的并行度。比如,开 10 个线程来推,这样我们所花的时间是不是就接近于原来的 1/10 了呢?</p>    <p>不过,如果有很多明星都同时发动态,我们的线程估计就要爆掉了。那又有解决方案没有呢?那肯定也是有的。既然这个地方会卡在同步的发送上,我们是否可以用异步的方式来推送呢?还记得分布式系统嘛?还记得我们的 Message Queue 嘛?我们就可以用这个系统来解决我们的问题。</p>    <p><img src="https://simg.open-open.com/show/138958618eb0f84e72fd7dee1a1dee7e.png"></p>    <p>当某一个朋友发送了一个新的动态,我们就将这个动态放到一个入口的发送器里( Poster ),这个发送器立刻将这个动态转发到传送的 Message Queue ( trans-mq )。 trans-mq 是多层级联,根据实际用户量来计算。最后一层的 mq 是用来做实际更新的( update-mq )。每一个负责一组用户的更新,将相关的动态塞到需要管理的那组用户的信箱里。</p>    <p>比方一个 update-mq 管理 100 万用户,那么我们如果有 1 亿用户,就只需要 100 个这样的 update-mq ,可能就只需要一个 trans-mq 。当某一个明星有发送新动态的时候,他的 1000 万粉丝散落在这 100 个 update-mq 管理的群组中,每个群组平均就只有 10w 个粉丝。如果写入每个 mailbox 还是 1 个毫秒,那么 10w 个粉丝就只需要 100 秒(大概 2 分钟)。这样,我们就把一个需要几小时处理完成的工作,变成了需要 1-2 分钟需要做的工作。为什么呢?因为他们并行起来工作了!</p>    <p>那是不是就完结了呢?并不是!如果发送的信息违反 xx 规定需要删除怎么办呢?有两种方式:</p>    <p>a 、同样的流程还需要走一遍:将删除操作推送到各个信箱;</p>    <p>b 、全局增加一个删除位,记录所有删除过动态:用户请求的时候,将动态 id 去这个删除系统里面查,看看是否有删除过。如果有,则过滤掉已经删除的动态。</p>    <p>好了,以上就是推送到信箱这种方式的工作原理和优化,怎么样,看懂了嘛?</p>    <p>B 、用时再 拉 取</p>    <p>这第二种方式呢,实际就是一种懒人模式( lazy mode )。他究竟有多懒呢?真的,很懒。如果你的好友发了动态,你永远不登录,这个动态就永远不会推送给你。只有你登录到动态页面,系统才会懒洋洋的把你的好友的动态抽取一遍。</p>    <p><img src="https://simg.open-open.com/show/5cff313002705a4d402d7923a7a7ea50.png"></p>    <p>上图大体描述了这个动态获取的过程:</p>    <p>a 、用户进入到动态页面(比如:朋友圈);</p>    <p>b 、获取好友列表;</p>    <p>c 、把好友的动态按时间倒排并展示。</p>    <p>大家注意到没有,整个过程中,用户已经没有了那个 mailbox ,取而代之的是动态的去拉取好友的信息。相对于之前聊的主动推送而言,这个就是一个被动的拉取过程。</p>    <p>由于是这样的一个工作模式,所以他有他的优点:当我关注了多个大 V 的时候,这些大 V 发动态不再主动推送给我,所以就避免了上千万次的主动推送过程。转而是用户浏览的时候再去获取。这使得我们的系统不会因为大 V 发动态而变得忙碌。</p>    <p>不过,如果我们的登录用户很多 && 他们都关注了很多其他用户,我们的系统就会有很大压力了:因为,他们要动态获取被关注者的信息并且做聚合。那我们有哪些办法做优化么?来看老王的才艺展示:</p>    <p>a 、限制关注的数量:大家可以看到,好多的动态系统一般都限制用户添加好友或者关注的数量。其中有一个原因就有可能是采用了这样拉取动态的手段。限制用户关注量以后,我们系统在合并的时候,就可以减少合并的数量,从而提升效率;</p>    <p>b 、为每个用户建立动态的缓存:我们可以给每个用户建立一个动态的 cache ,如果关注他的人取拉取他的动态,就不用每次都从磁盘(或者数据库)拉取,而直接从缓存中获取。从而提升拉取的速度;</p>    <p>c 、为聚合后的动态建立缓存:还是缓存方案,不过这次针对的不是产生动态的人,而是浏览动态的人。一旦拉取过一次以后,我们就将聚合过后的数据做一个缓存,下次再刷新的时候,就直接可以从缓存中获取(特别适合那种手不停的下拉刷新朋友圈的人 ~ )</p>    <p>d 、建立时间更新表:这个是个什么东东?如果我关注了 500 个人,如果平均每个人有 10 个动态,那一次给我 5000 个动态,对于我来讲,我会疯掉…… 所以,其实从产品上讲,没有必要将所有被关注者的动态都获取来展示,对吧。比如,我们可以获取最近有更新的 20 个或者 50 个被关注者,将他们的动态拉取做展示,对于绝大多数情况就非常适用。那种长期不更新的人,也没必要看,对吧 ~ 所以,我们可以为每个被关注者建立一个最后更新时间表,然后获取动态前,先看看哪些最近有更新,然后只拉取这些有更新的用户的动态做合并排序就可以了。</p>    <p>好了,动态用时方去拉,这种方法就差不多说到这儿。看懂了么?</p>    <p>在具体设计这个系统的时候,最好是根据产品业务形态、用户的数据规模等等综合来判断和考虑。两种方式各有优缺点,实现的成本也因规模不同。</p>    <p>a 、如果规模小,用最简单的推就可以了;</p>    <p>b 、如果规模中等,用简单优化过后的拉也可以了;</p>    <p>c 、如果是规模比较大,可以适度考虑优化过后的推或者拉,甚至推拉结合。</p>    <p>没有一个完美的架构,只有更适合业务的方案。所以,一切都取决于你的选择</p>    <p> </p>    <p>来自:http://mp.weixin.qq.com/s?__biz=MzA3MDExNzcyNA==&mid=2650392282&idx=1&sn=93fd63aea3ee3a3ccfc6cae43699fa65&scene=0</p>    <p> </p>