1、需求理解
朋友圈大家都熟悉不得了,不再赘述。
2、设计
这里要阐述一个最关键的设计点,那就是微信朋友圈具有明显的非实时性。不知道你有没有留意到,假如有两个人 A和B,其中A发了朋友圈,A是可以在朋友圈里马上看到自己发的那条朋友圈,但是对于B来说,B是不会跟A一样可以马上看到A发的朋友圈的,而是需要稍微等那么一下下才可以看到。
2.1异步发圈
如果选择同步发圈,那么要发送成功一条带有视频或者照片的朋友圈,必须会经过 【照片或者视频上传】、文字等信息到微信后台这么一个耗时的操作。那么就会面临一个想象,你发一个圈,就会那里转个圈圈,等个2秒。不可忍受。所以此时,可以选择异步发圈来达到用户快速发圈“成功”。
用户在客户端发圈时,内容信息可以首先是储存到客户端上,达到快速发圈成功。发布者就可以马上看到他自己的朋友圈了。但是这仅仅是一种类似“自娱自乐”的样子,因为别人根本看不到。
接着进行异步发布,客户端先把照片或者视频利用CDN技术,就近上传到CDN上,再将返回的照片或者视频地址以及文字发送到微信后台,进行储存。只有异步发送完毕,微信后台正常处理完成,别人即可看到这条朋友圈。
2.2 你的好友是怎么看到这条朋友圈的
你发的这条朋友圈(假设是公开的),你的好友在刷圈的时候 ,是会看到的。其实朋友圈跟QQ空间有一个很相似的点就是 它们是时间轴产物。顺着一条时间轴,在时间车轮下,你刷一下,时间轮滚一下。那么当微信后台接受你客户端发送的朋友圈的时候,要做的操作之一就是,获取你所有的朋友列表,做一个【离线批处理操作】,将这条朋友圈插入到这些好友中的时间轴里面去。这样你的朋友刷圈,根据自己的时间轴,就可以刷到了。刷的时候就根据图片url地址,去cdn拉取的。
时间轴怎么设计呢?说白了就是表,时间线表即可。
2.3 我的相册怎么实现
微信里面有个相册,就是你发过含有照片的朋友圈,那么就会在你的相册里,形成对应的照片内容。
这个跟上面类似。就是后台处理的操作之一,给你的相册写入索引,直接引向这些照片。里面存放的是对你的发布表里的数据引用,这样你以后浏览相册的时候,都是根据相册里的索引数据到发布表里找实际对应的数据的。
2.4 好友权限设计
朋友圈发的时候,可以选择给谁看或者不给谁看。甚至有你的朋友圈不给谁看或者不看谁的,还有什么几天可见等等权限设置。
如果按照普通的设计思路,由后台来判断我能不能看到这条朋友圈,那么必然会经过繁琐复杂的权限判断链路。权限是随时会变的,因为你可能今天能看到朋友这条朋友圈,明天你们可能吵架,拉黑了,你又不可以看了。刷圈是一个比较高并发的操作,如果全交给后台处理,那么要设计出高并发的接口,是很难的。
你有没有发现朋友圈没有下一页操作,都是滚轮操作。你也不知道你滚一下,新刷出来的是多少条。因为作为用户角度,你并不关心多少条,而是我滚一下,要有东西出来。
那么客户端就可以发挥作用了。
好友权限会同步在客户端存储一份。这个存储,可以大大提高系统抗揍能力。因为当你刷朋友圈时,读取自己的朋友圈的时间轴时,客户端可以利用这份权限数据进行数据过滤,然后进行合并。这样你刷朋友圈时,这一秒想看他的朋友圈,下一秒突然不看他的,那么都可以很快的满足你。当然后台也是会进行对应操作,只是不会如此及时。或者说就是那一秒你刷的时候,后台返回给你的时间轴,还是包含他那条朋友圈的,只是被你的客户端权限进行过滤了,所以你看不到了。
当然 权限这块 也不是谁都会动不动就改。它的频率甚至比你换微信头像还低。所以这块留有更有意思的设计,我相信微信官方是有精妙的实现方法的!
2.5 别人发圈,你的朋友圈上面的那个红点怎么实现
对你可见的好友发了的朋友圈,你的朋友圈角上会有一个小红点,注意不是数字,因为还没到评论点赞那里呢,别急。
这个又是怎么实现呢?
当你朋友圈时间轴被插入好友的朋友圈时,后台此时可以在redis,插入这么一个以用户为key,红点标志为value的数据。客户端与后台建立长链接,近实时感知我当前朋友圈的是否是红点状态。
微信目前用户还没有达到 13亿,而redis单机可以支持24亿key。可以干!
这里的长链接,可以借鉴nacos作为配置中心的经过优化的长链接设计。可以看我之前这篇文章从源码角度一步步窥探Nacos配置拉取与动态刷新、监听原理
2.6 点赞、评论怎么实现呢?
点赞很简单,就是给你点一下。那么我一直点点点呢?你是给我算几次?微信里是算一次!那么就意味着要去重,后台数据库表也不能这么暴力存储每一次点赞记录,然后就去重取一条。那这样必然搞垮你的数据库。
朋友圈的点赞和评论,是独立的数据,其实比如点赞,都是可以基于redis来做的,每个朋友圈里对应一个set数据结构,里面放谁给你点赞了,这样每条朋友圈的点赞人和点赞数量直接从redis出就可以了,可以实现高并发点赞、去重、计数。
假设通过数据库的表来存放点赞的数据,此时可以通过联合唯一键来保证幂等性,用户id+朋友圈id是一个联合唯一索引,就可以确保一个人对一条朋友圈只能点赞一次,如果通过redis里的set来实现,set本身就是可以去重的,你多次把一个人放到一条朋友圈的set里去,是不会重复的。这样利用sismember就可以找到哪些人给你点赞,scard就可以立刻得到点赞人数。
评论也是可以存表里的,都是以朋友圈为粒度来存储。评论可以发多个一模一样的,所以没要进行去重。但是需要计数。因为到时候朋友圈角上显示的数字是包含评论数的。
那么问题来了,就是共同好友问题!!!你给A点了个赞,A的好友B也给A点了赞,但是你跟A是共同好友,那么此时你也会跟着会有朋友圈角上有数字的提醒!你打开一看,你只能发现这条朋友圈点赞的人就只有你们的共同好友B,其他的点赞人你都看不到,因为那些都是A的好友!
那么你客户端用sismember时得到这条朋友圈的点赞人列表时,还需要跟自己的好友列表进行交集操作,找出共同好友来进行显示。
那么此时,这个朋友圈角上的数字,就有点不太好处理。首先之前做朋友圈那个红点显示,其思路是可以继续用在这里的。但是处理就没之前那么简单了。
首先,先不管数字多少。当我们发现数字,点开朋友圈,点击顶部中心的条目提醒时,就会弹出所有提醒信息,包含点赞、评论。一旦退出,就意味你全部已读!意思就是,不管怎样,一旦你点了,就“强迫”已读。所以这就好办了。将计数存进redis,一旦点了,则置空计数,重新计数。
之前做朋友圈红点显示,用的是string 数据结构,现在将其改为hash。key依旧为用户id,hash里面的第一个值为key为红点标志,value为是否。第二个值为key为红点数字,value为具体数值。
那么已读已经解决。现在来看看怎么进行计数累加。
当客户端来获取数值时,后台则直接向redis这个hash取相应的红点数字即可。
重点是在进行点赞或者评论时。我们来看画个图感受一下。
所以点赞或者评论时,各自客户端需要根据自身好友列表与当前该评论所属的用户拥有的好友进行交集操作,先找到共同好友,再根据好友权限,选择性去给可见的共同好友 加上 该朋友圈的主人进行计数累加提醒,就是在redis里,给对应的用户id为key的相应的红色计数进行累加。
有redis在,它的性能就不会差到哪里去。当然这些数据不能一直存到redis里啊,微信10亿用户,这么搞,肯定不行。这些必须都将落库持久化。因为你的朋友圈只关注最新的!其他的基本都会慢慢尘封!
3 结尾
这仅仅是我的猜测与我自己的设计罢了,并不是微信朋友圈真正的设计实现。
本文就到此结束了,我们下文见,谢谢
github: honey开源系列组件作者