如果让你设计一个微信朋友圈

业余杂谈 herman 65浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:codedq,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:codedq,之前的微信号好友位已满,备注:返现
饿了么大量招人,我内推!Java 方向!薪资不设上限,工作年龄不限!工作地点限魔都,可电话面试!简历,发我微信:codedq
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

最近在面试一些高级工程师,很多人都自称自己工作经验丰富,精通各种并发问题。于是,我就问了问,如果让你设计微信朋友圈,你该如何设计?

有人直接说,微信朋友圈不就是一个加强版的论坛吗?有什么难的。

说这话的一定没有并发经验,也没遇到过海量用户的存储实战。要知道,微信可是 10 亿级别的用户量。

首先在数据存储上,必须采用跨数据中心(IDC)多副本的架构。其次,想设计好朋友圈,必须要对因果一致性做到精通。

微信朋友圈技术负责人曾在 2015 年 ArchSummit 全球架构师峰会上分享过一些关于朋友圈某一条状态的评论以及评论的回复的设计,非常值得我们思考和借鉴!

因果一致性的认识

问-答的因果关系

上面就是一个因果一致性问答系统的设计图。一个对话的观察者(Observer)首先看到问题的答案“About ten seconds…”,然后才看到被回答的问题“How far into the …”。这是令人困惑的,因为它违背了我们对因与果的直觉:如果一个问题被回答,显然问题本身得先在那里,因为给出答案的人必须已经看到这个问题,我们认为在问题和答案之间存在因果依赖。

考虑我们设计这样一个微信朋友圈,当有一个用户去访问数据,例如刷新最新的所有问-答列表,就像刷新知乎推荐页面一样,他一定要先看到问题,然后再看到答案,否则就会给用户带来很大的困扰。因为只看到答案,而没有相应的问题是没有实际的意义的。

因果关系对事件施加了一种顺序:因在果之前,消息发送在消息收取之前。而且就像现实生活中一样,一件事会顺序地导致另一件事发生:某个节点读取了一些数据然后写入一些结果,另一个节点读取其写入的内容,并依次写入一些其他内容等等。这些因果依赖的操作链定义了系统中的因果顺序,即什么在什么之前发生。从而我们也引出了分布式系统的因果一致性,如果一个系统服从因果关系所规定的顺序,我们说它是因果一致性的。

微信朋友圈的因果一致性

下面我们来看微信朋友圈某条状态的评论以及对评论的答复(也是评论)所构成的因果关系,以及微信是怎样通过保证不同数据中心间的因果一致性来保证一个用户在刷朋友圈的时候不会出现看到评论所对应的答复,却看不到答复对应的评论。

微信朋友圈的因果一致性设计

从上面的简图可以看出,微信分布在全球四地的数据中心,用户小王有两个朋友:Mary、Kate,分别在不同的区域下(数据中心),所以他们要看到彼此朋友圈的内容时,必须等到相关的数据在不同数据中心间的副本同步到用户所在的 IDC 完成之后才能看到。

微信朋友圈的赞与评论的写因果一致性
数据在副本间同步时乱序

从上面的 2 张图中可知,由于网络在不同副本间复制数据时的延迟、中断等分布式系统中常见的场景,导致两条消息在同步到用户 Kate(加拿大)所在数据中心上的副本时已经乱序了。即原先顺序是这样的:“Mary:这是哪里?”->“小王:Mary,这是梅里雪山”,然而 Kate 去数据库中查到的消息却是这样的顺序:“小王:Mary,这是梅里雪山”->“Mary:这是哪里?”,或者中间的某个时刻只能查询到“小王:Mary,这是梅里雪山”这一条消息,你说 Kate 会不会懵逼。

为了解决这个问题,微信是怎样来处理的呢,且看下面分析。

因果关系的梳理
因果关系的一致性算法

从上面两张图中可知,我们可以将 Mary 对小王所发布的朋友圈状态的评论“Mary:这是哪里?”当成因,而把小王对 Mary 评论的答复“小王:Mary,这是梅里雪山”当成果。按照这样的约定,当这两条数据同步到 Kate 所在的数据中心副本时即使发生乱序,Kate 根据在刷朋友圈时,根据因果关系也可以将这个评论、答复的顺序调整到正确的、可阅读的方式。那微信到底是采用什么方法来让各个地区的用户理解这个约定呢?具体来看:

朋友圈事件因果一致性的算法

从上图我们可知,微信采用如下的方案:

  • 每条评论都有一个唯一的且递增的数字ID,确保排重
  • 每条新评论的ID都必须比本地已经见过的全局最大的ID大,确保因果关系
  • 广播本地看到的所有评论和新评论到其它IDC;相同ID的评论合并排重

我们可以针对上面的三点背后的技术作出合理性的解读跟假设:

1、每条评论都有一个唯一的且递增的数字 ID:那么背后肯定是一个 ID 生成器,各个数据中心都有一个这样的入口来获取本 IDC 内唯一、递增的 ID。具体怎么做的可参考。

2、每条新评论的 ID 都必须比本地已经见过的全局最大的 ID 大,确保因果关系:如上图在香港的数据中心,当发表完2的评论,并且已经同步上海数据中心过来的1 4 7等 ID 的评论之后,如果再有香港地域下的用户发表新评论时,那么一定要大于当前香港数据中心能看到的全局最大 ID,此时是 7,所以香港地域此时用户最新发表的评论的 ID 必须大于 7(上图有一个“跳过5”的备注),所以上图中的 ID(8)就是从这里得出的。

3、广播本地看到的所有评论和新评论到其它 IDC;相同ID的评论合并排重:那么什么时候广播呢?其实就是本地域下的用户针对同一条朋友圈状态有评论时,该地域就负责申请一个全局 ID,然后将这个评论的事件广播给其他的数据中心。注意这个过程需要合并所有看到的序列,例如香港数据中心就合并1 2 4 7 8等针对同一条朋友圈状态的一系列评论事件 IDs,然后再整体广播出去,这样才能保证针对同一条状态的所有当前最新的事件整体被广播出去,否则此时香港 IDC 只广播 8 的话,如果前面的事件序列在广播的中途丢失了,那么其他节点比如加拿大 IDC 就会漏掉部分评论事件,这也是数据多重补位的措施。当然这个方法有一个前提就是:因为同一个朋友圈的发布状态,一般的评论不会很多,所以造成的数据冗余交互不会很大,否则是不行的。至于相同 ID 的评论合并排重,上图 5 可以看出,加拿大IDC会收到来自上海 IDC 的1 4 7事件系列,也会收到来自香港IDC同步过来的1 4 7 8 事件系列,这两个广播的事件系列有重复,所以需要去重。

总结

最近正在阅读一致性相关的书籍,因果一致性只是一致性算法中的一种,有对这方面有见解的,可以加我 WX,分享你的经验和见解!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:xttblog(5000人已满),微信号3:codedq(超3800)。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 如果让你设计一个微信朋友圈