MyException - 我的异常网
当前位置:我的异常网» 移动开发 » 最全面图文讲解:Twitter怎么支持上亿用户

最全面图文讲解:Twitter怎么支持上亿用户

www.MyException.Cn  网友分享于:2013-12-18  浏览:0次
最全面图文讲解:Twitter如何支持上亿用户
谈到设计Twitter, 我们首先要问一个本质问题:设计Twitter的基本方法论是什么?

其实是我们计算机设计最基本的方法:分治法(Divide and Conquer)。

什么是分治法呢?就是把问题不断的拆解,拆解到你可以解决为止,它的艺术在于,从哪个维度来拆解非常考验我们能力。

如果要求一周开发出Twitter,你会怎么做?

你的架构是什么样的呢?




相信你一定不会给出复杂的架构。前端是各种各样的业务逻辑,后端是MySQL数据库,这样就够了。因为这已经解决了当时的问题,满足了一周开发出来的要求。

但随着Twitter的成长,我们会遇到各种各样新的挑战:

MySQL难扩展。
为什么难扩展?因为它把同样的一个数据分成各种关系存在里面,每次取的时候,都要通过Join来进行复杂的操作,这个Join当数据被切分的时候存在更多的服务器上,会变得越来越复杂,所以很难扩展。

小变化也要全部部署。
任何变化都需要部署到所有机器上,因为服务不断升级,就变成了每天不断部署,变成了daily deployment。所以每次部署的时候耽误时间很长。

性能差。
因为所有服务都要部署在一起,造成了它的内存占用率大,而且部分核心模块还因为最初当时的单线程设计成为了各种瓶颈。

架构混乱。
因为所有模块在一起很混乱。

那要怎么破解这些问题?

答案是将问题拆解开来。


第一刀将存储切开。

我们看后台,可以拆成存Tweets,存User,存Timeline,存Social Graph不同内容,Timeline可以拿Redis这样的数据库来保存,而对于其它的数据比如Gizzard其实是一个分布式的MySQL数据库。并不是所有数据都不适合用MySQL分布,我们可以把这些适合的用MySQL来shutting一下,而不适合的用别的数据库来存,就是一个存储切开的方法。





第二刀是将路由、展示、逻辑切开。

我们可以看到又多出了一层,逻辑层。Tweets,User,Timeline它们分别对应后边的各种数据存储。前端会有外部访问的接口,API访问接口,并且它还保存了以前数据的整个架构,用来进行一些小规模的使用。最前面需要有一个Routing,来将不同的请求分布到不同的API上。所以之后Twitter就变成了上百上千个小小的数据模块,包括它的服务模块,他们通过之间的相互调用来完成具体的请求。




如果Lady Gaga发了个推文,会发生什么呢?

首先她发出一个推文,会到达最上面一层的外部模块,负责把推文写出来;接着到了API模块,负责接收这个推文;再往后是Fanout,Fanout将推文的ID推荐给所有订阅这个用户的信息收件箱里,收件箱就是Timeline,比如有400万人订阅了Lady Gaga,就会有400万的收件箱收到这个消息;紧接着由于我们Timeline要发到收件箱里,收件箱必然是一个最复杂的操作,为了优化它的性能,就把它们用分布式的方式存储在Redis里面,Redis是偏向内存的数据库,所以能够很快的存储这些信息;但Redis里存的只是ID,所以当一个用户具体要她推文的时候,实际上要通过Timeline服务去找到这个用户对应的是哪个Redis服务器上存了推文的 Timeline list,接着找到所有ID,然后这个Timeline service把ID的具体内容通过另外的数据库装载进来,最终得到结果反馈给用户。这些保证了我们推整个数据的时候速度最快,能够达到每秒30万次的性能。




虽然有了这样的过程,如何支持搜索呢?

很简单的想法是当我们写入API,它Fanout到每个用户的Timeline list的时候,我们可以拿另外的Ingester把这个推文放到里面,Ingesert最后把它放到Earlybird,就是所谓的倒排索引中。比如有个推文发过来“我喜欢太阳”,就把我,喜欢,太阳,拆成三个词,把这些单词,进行倒排索引,存到Search index里面。这时候实际会用到很多很多Earlybird,这样就能建立很多倒排索引,能够并行的去做。当用户一个请求过来之后,比如搜索“早上吃饭”,就会把morning和eat作为两个关键词发到我的Earlybird集群里,得到结果后Blender会把它组合到一块,并反馈结果。当然很多用户可以并行的向Blender发送请求,从而得到最终结果,这就是我们的搜索服务。




如何通知用户新消息到达呢?

第一个方法是另外再开一个Write API,在有新东西发生以后,我把它放到一个Push的服务里,那所有用户只要都连到这个后台里,HTTP PUSH,就会通知他有新消息产生了。同理对Mobile ,会有Mobile Push,当然大家对不同的信息有不同的对接方法,大家可以去仔细考虑下怎么能做到这个样子。




如何搭建这样的服务呢?

最基本的是开源项目Twitter-Server。
配置服务,IP之类;
管理服务,哪些down了、控制、启动等;
日志服务,运行怎么样,以后出问题找谁等;
生命周期服务,什么时候启,什么时候关,什么时候控制;
监控服务,到底有没有出错,出错以后怎么办,互相报警等
这些东西合到一起,就构成了这样服务的基本架构。


各个服务之间如何交流?

这就是传统的RPC(远程的进程调用),大家能够通讯的不仅是数据,而且可以通讯命令或请求之类的。这时需要开源项目Finagle。
能够提供服务发现,因为有很多服务,所以要找谁发送这个请求呢;
负载均衡,可能有十个人提供服务,先放到谁那儿?
重试,如果失败了怎么办,是否需要重试?
基本的线程池和链接池,大家可以复用,不用每次去创建,浪费资源了;
统计信息的收集;

[b]分布式调试。[/b]

如何调用一个服务?

因为从A调用B,大家之间各种远程,写代码上要怎么做呢?可以使用函数来调用(Service as a Function),背后实际上是Function programming的思想。

可以看下这个基本例子:trait Service[Req,Rep]extends(Req=>Future[Rep])

简单理解为:我有一个请求想得到一个Response,就是Request到Response。这里面实际上可以先面向未来实现,之后当它执行的时候,就会得到相对应的结果。


多个服务的调用如何整合在一起?

我们看一看它要如何运行?比如我们有一个请求是得到一个用户的所有Timeline数据。它其实有很多步,第一步是得到User ID;第二步得到User timeline list里的那些消息ID;下一步是针对每一个消息,得到它的每一个具体内容,比如“我早上吃饭”这些内容,这些内容里面可能还有图片,还需要得到图片的数据。这是一个很复杂的过程,但这个箭头表达了它们最基本的执行顺序。






最佳的执行路径是什么?

首先是得到ID,再得到Timeline以后可以并行地读取每个tweet,可能会有的快有的慢,然后又得到一些它的具体信息,比如说图片之类,所以这就是最优策略。





下面我们来看一看代码:




第一行是得到用户ID,用一个面向未来的方程得到一个用户ID。





第二行根据ID拿FlatMap,FlatMap其实是后面针对每一个ID执行的函数,这个函数来得到这个ID用户的Timeline list。所以这也是我们Function programming,或者如果用Spark也会经常碰见的函数。





得到每一个ID以后,实际上要针对每个ID执行,所以需要Map。Map每一个ID是做什么的,得到这个Tweet的具体信息。代码第二行就是根据ID得到他的具体内容。





当然如果这个东西有图片,那需要得到图片。





中间一步还需要把这些东西并行起来,大家都去单独得到Tweet,并且把它集合放到一块儿去。整个过程我们就得到了最终的代码。





这其实就是第三刀:切开“做什么”和“怎么做”。在这个代码过程中,所谓面向函数的编程,只是写了想做什么事儿,但具体怎么做,比如从哪个机器找、连接服务器、从谁那儿抓取,怎么并行等都没有写,实际上这些东西都被封装到了一个底层的库里面。所以现在Twitter就可以有两个团队,一个团队负责写代码,“做什么”;另一个团队负责写底层是怎么实现的。这样就能实现并行开发,这也是Function programming的一个好处。

一个服务器的架构长什么样呢?

上方是你的服务,下方就是你的服务器,包括一些集群的管理、内存、Java的虚拟机、操作系统和硬件。





有很多服务如何整合?

结果整合到一起就是很多层,底层都是一样的,但上层跑了不同的服务:HTTP、聚集器、时间timeline服务等。我们所有东西加在一起,系统就会跑上千个不同的小服务器,而且之间会有各种各样的备份。




那如何来统计出现的服务器情况呢?

比如会统计平均延迟,或统计一些信息来验证服务器是不是出问题了,实际上,在这种大规模里面,一个比较好的方法就是不要拿平均值统计,因为特殊情况会拉低所有情况。举个例子,假设北京有一个人,他年收入是100万,另外99个人每个人年收入都是1元。那平均下来每个人收入都是1万块钱,但实际上大多数人是很穷困的。所以不能拿平均数来算,尤其是在服务器的情况下。所以大家一般都会取中位数(median),百分之90的点,百分之99的点和百分之999的点。我们可以看到,下图是一个服务发生故障的前后。在服务故障发生前,百分之99点的平均延迟是100毫秒,但故障发生后变成400毫秒,发现这个问题以后就会去抓。但如果用平均值,很可能就发现不了这个问题。

这样还有一个好处,当我们做到上面整合的架构以后,每一个负责写代码的团队,只负责处理跟它相关的上下层就行,而不用做那么多交互,所以每个人的东西都非常简单,这也是所谓微服务的一个好处。


如何监控一个服务呢?

实际上针对这个服务在整个栈里面用的时间,会有一个图表达出来。






如何监控一个请求呢?

实际上,基于一个请求,会有一个整个运行的最佳策略,这就需要运行的整个过程的图。这就是我们之前讲的,得到一个用户所有Timeline内容信息的锯齿图,Zipkin,这也是一个开源项目。





如何监控系统的运行情况呢?

因为这个时候失败已经成为常事了。举个例子,假设每秒有30万的请求,有99.99%的成功率,那每秒一定会有30个失败的,所以不能说每次失败怎么样,统计失败率会比单独的失败更重要,而且写代码的时候要为这个失败来进行自动重试。大家不用纠结每次的失败,只要大部分过去就行了。


最后做下总结
学好分治法,走遍天下都不怕。
函数式设计切分做什么和怎么做。做什么是由函数式设计来写,而怎么做由底层的语言和编译器来优化。
面向错误让我们使用了统计域监控。99.9%的点出什么问题了,这样就叫做基于统计域的监控方式。

PS:如果你想成为一名优秀的架构师,或者在工作中遇到瓶颈,想跳槽加薪,面试不过,
碰到难题等等一系列问题,可以加我的架构师群:554355695
这里有最专业的团队为你排忧解难,有最新的学习资源为你共享。











文章评论

程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员必看的十大电影
程序员必看的十大电影
老程序员的下场
老程序员的下场
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
漫画:程序员的工作
漫画:程序员的工作
程序员应该关注的一些事儿
程序员应该关注的一些事儿
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
编程语言是女人
编程语言是女人
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员都该阅读的书
程序员都该阅读的书
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
程序员的鄙视链
程序员的鄙视链
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
我是如何打败拖延症的
我是如何打败拖延症的
我的丈夫是个程序员
我的丈夫是个程序员
总结2014中国互联网十大段子
总结2014中国互联网十大段子
如何成为一名黑客
如何成为一名黑客
那些争议最大的编程观点
那些争议最大的编程观点
旅行,写作,编程
旅行,写作,编程
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
为什么程序员都是夜猫子
为什么程序员都是夜猫子
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
 程序员的样子
程序员的样子
程序员和编码员之间的区别
程序员和编码员之间的区别
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
鲜为人知的编程真相
鲜为人知的编程真相
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
Java程序员必看电影
Java程序员必看电影
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
代码女神横空出世
代码女神横空出世
10个调试和排错的小建议
10个调试和排错的小建议
每天工作4小时的程序员
每天工作4小时的程序员
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
中美印日四国程序员比较
中美印日四国程序员比较
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有