系统构架 | iJohn.org

Archive for the ‘系统构架’ Category

9th
八月 2011

Big Data in Real-Time at Twitter
爱因万江斯坦@2011年08月09日 15:27 Post in 系统构架 No Comments »

15th
七月 2011

”Twitter搜索现在快3倍啦“
爱因万江斯坦@2011年07月15日 09:49 Post in 系统构架, 性能 No Comments »

在2010年春季,为了服务不断增长的流量、提升最终用户的响应时延和服务的可用性、有能力快速开发新的搜索功能,搜索团队开始重写我们的搜索引擎。作为不努力的一部分,我们发布了新的实时搜索引擎,将后端从Mysql迁到了lucene的实时版。上周我们发布新的前端,替换了Ruby on Rails版:我们称之为Blender的Java服务器。我们很高兴地宣布这些改变使我们的搜索时延下降了3倍,同时也使得我们有能力在接下来的几个月里快速迭搜索特性。

性能收益

Twitter搜索是世界上注量最重的搜索引擎之一,每天服务超过10亿的查询。在我们发布Blender的前一周,日本 #tsunami 产生了很大的查询负载,并产生了查询响应时间的峰值。随着Blender的发布,我们的95%时延减少了3倍,从800毫秒到250毫秒,同时前端的CPU负载降低了一半。我们每台机器的容量能服务10倍的请求增长。这也意味着我们能能够用更少的机器支持同样的请求数,从而降低前端服务的成本。

Twitter搜索现在快3倍啦

blender发布前后的95%的查询接口时延对比图

Twitter改进后的搜索架构

为了弄明白这些性能收益,你首先必须明白我们以前的RoR前端是如何地低效。前端运行固定数量的单线程工作进程,每一个做下面的事情:

. 解析请求
. 同步查询索引服务
. 聚合并展现结果

我们很早就知道,同步方式处理请求会低效地使用我们的CPU。经过一段时间,在Ruby的代码层累积欠了重大的技术债,这样很难增加新特性和改进我们搜索系统的可靠性。

Blender通过下面的方式解决了这些问题:

 创建了一个完全异步的聚合服务。没有线程会去等待网络IO完成
. 从后端服务中聚合结果,例如,实时的,最流行的微推和地理分片
. 优雅的服务之间的依赖关系处理,工作流自动处理后端服务之间的传递性依赖

下图显示了Twitter的搜索引擎架构。从网站、接口或内部客户过来的查询请求通过硬件负载均衡设备转发到Blender上。Blender解析请求,然页转发给后端服务,使用工作流来处理服务之间的依赖。最后,将后端服务返回的结果进行合并,以合适的语言呈现给客户。

Twitter搜索现在快3倍啦
Twitter跟Blender有关的搜索架构图

Blender简介

Blender基于Netty http://www.jboss.org/netty打造的Thrift和HTTP服务,Netty是用Java写的高扩展的客户和服务器库,基于它能快速地开发各种协议的客户和服务器端程序。我们没有用其它产品,如Mina和Jetty,主要是因为在其它项目中已经用过它,当然它的接口更简洁、文档也写得不错,这也是我们用它的原因之一。为了让Netty跟Trift一起工作,我们写了解析Thift的代码,专门解析从Netty的channel缓存中的过来的Thift请求,当它从socket读请求或需要封装Thrift响应时,都会调用它些代码。

Netty定义名叫通道的抽象功能,用来封装到网络套接字的连接,提供读、写和连接等IO请求的接口。所有通道的IO请求天然都是异步的。这意味着任何IO调用立即返回一个通道实例对象,以通知请求是否完成、失败或被取消。

当Netty服务器收到一个新的连接,它会创建一个通道流水线来处理这个连接。一个通道流水线不是别的,其实就是通道处理程序的序列,通过它实现处理连接的业务逻辑。在随后的一个小节里,我们会说一下Blender是如何将这些通道流水线映射到查询的工作流处理上的。

工作流框架

对于Blender来说,一个工作流就是一组后端服务,服务之间有依赖关系,每个后端服务都需要处理才能处理收到的查询请求。Blender自动解决后端服务之间的依赖,例如,如果服务A依赖于服务B,首先会请求A,把它返回的结果返回给B。工作流可以很方便地用有向无环图来表示(见下图)

Twitter搜索现在快3倍啦
6个后端服务的Blender工作流示例图

在上面的示例图中,我们有6个服务{s1,s2,s3,s4,s5,s6},之间有依赖。从S3到S1的边意味着先要访问S3,之后才是S1,因为访问S1时需要访问S3后的结果。对于这样的一个工作流,Blender框架先会进行拓扑排序以确实所有服务的顺序,它们也必須以这样的顺序依次调用。上图的服务执行顺序将是{(s3,s4),(s1,s5,s6),(s2)}。这也表明s3和s3可以在第一轮里并发调用,一旦它们返回结果,S1、S5和S6可以在第二轮里并发调用,最后才调用S2。

一旦Blender确定了工作流的执行顺序,工作流将会映射成Netty的流水线。流水线是一个处理程序序列,请求依次会通过这些处理程序。

复用进来的请求

因为工作流映射成了Netty的管道,我们需要将进来的客户端请求转发给合适的流水线。因为这个原因,我们设置了一个代理层,它负现复用并转发客户端请求到流水线上:

. 当一个远程Thrift客户端跟Blender建立一个长连接时,代理层会创建一组本地的客户,跟每个本地的工作流服务对应。注意当Blender进程起动时,会在同一个java虚拟机里起动所有的本地工作流服务
. 当从套接字上收到一个请求时,代理层读取这个请求,计算出需要请求那个工作流服务,然后转发给对应的工作流服务
. 类似地,当收到本地的工作流服务的响应时,代理层读取结果,并把结果转发给远程的客户

我们利用Netty的事件驱动模型来异步完成上面的所有任务,这样也没有任何线程需要等待IO。

分配后端请求

一旦请求转到了工作流流水线,将依次由工作流定义好的服务处理程序处理。每个服务处理程序创建一个合适的后端请求,转发给远程的服务器。例如,实时处理程序会创建一个实时搜索请求并异步地将它发给一个或多个实时索引服务器。我们使用twitter commons库https://github.com/twitter/commons(最近开源了!)来提供连接池管理、负载均衡和死主机检测。

当请求分配给所有后端之后,请求对应的IO线程就将被释放。一个定时线程每隔几毫秒就检查有没有后端的请求返回,并设置对应的标记,表明请求成功、超时或者失败了。在一个查询请求的存在周期内我们为它维持一个对象,用来记录上面这些状态信息。

成功的响应会聚合在一起,再发给工作流中的下一批处理程序。当第一批所有请求的响应的返回之后,将异步地发出第二批请求。这个过程会一直重复直到我们完成工作流或者超出工作流设置的超时时间。

你能够看出,在工作流执行期间内,没有任何线程会去等待IO。这让我们有效地利用Blender机器的CPU,并且处理大量的并发请求。我们能将大量请求并发发给后端去执行,从而也能减少请求的时延。

Blender部署及以后的工作

为了在我们的系统中增加Blender的时候,能确保提供高质量的服务。我们用老的RoR前端作为代理,转发请求给Blender集群。这样使得我们在对后边的技术进行大的调整时,给用户一个一致服务体验。我们下一阶段部署时,会从搜索的服务栈中完全去掉RoR,让用户直接去连接Blender,从而进一步降低访问时延。

原文链接:
Twitter Search is Now 3x Faster

21st
四月 2011

天涯社区的进化经验(一)技术篇
爱因万江斯坦@2011年04月21日 13:05 Post in 系统构架, 性能 No Comments »

设备投入
– 1999 1台服务器
• 2000年,在线用户300
• 2002年,在线用户1000
• 2003年,在线用户8000
WEB+DB共用在一台服务器上
– 2004 4台WEB 2台DB
• 2004年,在线用户2万
WEB及DB剥离。同时WEB不断增加,DB进行分库(4台WEB,2台DB)
– 2005 7台WEB 4台DB
• 2005年,在线用户14万
– 2006 11台WEB 4台squid(页面缓存) 6台DB
• 2006年,在线用户20万
– 2007 +双线接入 F5设备
实现双线双IP,购置F5实现本地及全局负载均衡,haproxy部署,动静剥离
– 现在 136台应用 38台DB
2008年,数据库双机(事务复制),IBM架构咨询。
• 2009年,在线用户40万
Memcache,数据访问层,服务器虚拟化。
• 2010年,在线用户54万

大多数网站发展初期遇到的技术问题,不是没有采用多层架构、没有使用内存缓存、没有购买好的服务器,而是数据结构及检索设计出了问题。
数据库调优是一个长期的过程,建立模拟环境,来跟踪每条请求的反应时间。

数据库设计注意事项
• 在对产品及业务深度了解的前提下进行数据库设计
• 表记录规模的增加对查询不要有性能问题
• 合理的索引(不要犯常识性错误)
• 慎用联合查询(几乎不用),通过一定的数据冗余来回避联合查询
• 必要时通过合并数据,减少结果集大小
• 尽量通过WHERE来定位分页数据
• 在设计时尽量考虑数据规模增长后的拆分问题

WEB服务扩展
• 无状态服务
– 不要用session保留用户数据,Cookie(或会话Cookie)保留用户标示,用户数据统一保留在数据库,或内存中)
• 应用拆分
– 按不同业务拆分,按流量拆分
• DNS轮询、或通过应用控制轮询

构架完善:
双链路接入双IP
– 使用了2台F5-GTM-1500做链路判断。
• 负载均衡
– 使用了2台F5-LTM-6400实现负载均衡
– LVS(IP层负载均衡)
– 反向代理(haproxy 7层负载均衡
• 动静分离
– 使用lighttpd来提供静态资源服务。
• 代理服务
– 使用haproxy提供七层负载均衡,通过ACL拆分应用。
• 页面缓存
– 2005年,使用squid-2.6在前端实现页面级缓存。
– 已经替换成varnish,增加压缩模块。
• 内存缓存
– Memcache

建立服务可用性监控
• 要预警及报警机制和故障点快速定位
– 应用运行中的错误监测
– 应用可用性监测
– 服务器及网络设备健康监控
– 网络流量监控
– 全国访问质量监测

14th
十二月 2010

Ajaxifying, Caching and Pipelining Facebook Website
爱因万江斯坦@2010年12月14日 20:54 Post in 系统构架 No Comments »

3rd
十二月 2010

8个常用于可扩展系统的设计模式
爱因万江斯坦@2010年12月03日 02:35 Post in 系统构架 1 Comment »

Ricky Ho 在文章 Scalable System Design Patterns 中列举了一系列构建可扩展系统的优秀实践,以下是对这些优秀实践模式的一个摘要:

1,负载均衡
– 把一个请求按一定hash算法或规则分配到服务器组中的一台去处理,以分担单个服务器的压力。这一般多见于大型网站的构架。

2,分头收集(Scatter and Gather) –  把一个请求分解成好几个服务请求分发到多个server上,每个server处理后返回的结果会被合并成一个返回结果给请求端。 常见于搜索引擎如google,百度,搜狗,对一个关键词的搜索结果是由多台server处理并合并成一个搜索结果页。
3,结果缓存 – 服务器缓存某个请求的结果,下次对同样的请求只返回缓存的结果就ok了,而避免下次同样的请求进来时去做重复的计算。Memcached就是做这个用的。

4,空间共享 – 分布式计算常用的模式,所有的数据、对象都放在一个共享虚拟空间,所有的计算进程共享并控制这些数据。


5,管道过滤
– 所有的请求都先进入某个管道,然后以先进先出的方式接受处理和返回结果。有点似曾经写的投票,推荐这样的应用时的处理方式。

6,MapReduce –  在处理批量任务时,如果磁盘I / O是主要瓶颈,则一般采取这种模式。它的使用分布式的文件系统,从而使多个I/O操作能够并行。这种模式在谷歌的内部应用程序中使用的较多,开源的Hadoop就是个典型案例

7,批量同步并行 –  该模式下的所有任务是基于锁步执行,由Master来协调。每个任务重复以下步骤,直到再没有活跃的任务。
每个任务从输入队列中读取数据
每个任务根据自己读取到的数据进行处理
每个任务将自己的处理结果直接返回

8,Execution Orchestrator – 该模型是基于一个智能调度的任务分配,还没太弄明白。

25th
一月 2008

Flickr构架
爱因万江斯坦@2008年01月25日 22:36 Post in 系统构架 No Comments »

相关资料:
http://www.bytebot.net/blog/archives/2007/04/25/federation-at-flickr-a-tour-of-the-flickr-architecture
http://www.niallkennedy.com/blog/uploads/flickr_php.pdf
http://qcon.infoq.com/sanfrancisco/file?path=/QConSF2007/slides/public/IanFlint_YahooCommunitiesArchitecture.pdf
http://gocom.primeton.com/modules/newbb/item43643_43643.htm
还推荐一本fickr的资深构架师 Cal Henderson 的大作《Building Scalable Web Sites

截止到2007年11月13日,Flickr 用户上传的图片已经达到20亿,Flickr必须每天处理海量更新的图片,还有不断增加的用户,竟然还能不断发布新的功能,同时也保持着良好的应用性能。从Cal Henderson 的书中,我能感觉Flickr的成功是与他们坚实的技术和构架密不可分的。

flickr-4
平台
ok,先来看看他们的平台:
PHP
MySQL
Shards (这是个新名词,还是叫”碎片”吧,”指的是将应用的数据横向拆分,也就是说如果有几千万的用户信息,那么这些用户信息可以被分布在多个数据库服务器上“)
用Memcached做缓存layer
对Html和图片用squid反向代理
RedHat Linux
Java,Perl,用Pear处理xml和email
Apache
ImageMagick ,SystemImager ,Ganglia(分布式监控程序),Subcon(用于管理和发布集群机器的配置文件),Cvsup(用于在网络中分发和更新文件)

flickr-2

数据库  

dualtree中心数据库中存储像user这样的表,但只存储它们里的主键id和具体应该指向哪个分布数据库的指向信息。每个 Shard存储40W的用户数据,大多数数据会存储两次,比如,一个评论的发表,它是介于被评论对象和发起评论对象之间的数据,这种数据,flickr会存储两次,通过事务来同步执行:打开第一个事务(transaction),写数据,打开第二个事务,写数据;如果两个事务都正常执行了,则第一个事务提交(commit),如果第一个事务提交成功,则第二个事务提交。这个流程,如果遇到服务器down机,其实还是有可能出现只提交了一次的情况。

Flickr的搜索功能有两个结果返回来源,一个是Flickr自身的Shards机器,另一个是Yahoo的web搜索,用户自己的tag搜索走的是Flickr自己的Shards方式,其它的就是走的Yahoo的web搜索了,这个改进肯定是在被Yahoo收购之后的事了。

看资料,Flickr的服务器确实够强:EMT64 w/RHEL4, 16G的内存,6块15K RPM硬盘组成的RAID-10盘阵(羡慕…)。用户数据目前有12TB,当然这不包括图片,图片内容比这点数据要多的去了。都是2U的机器,每个数据库shard存有120G的数据,也就是说Flickr有10台数据库shard机器。

 

flickr-3

 

 

 数据备份

Flickr这种提供图片服务的web2.0网站,数据尤其重要,所以他的备份工作显的格外重要:
采用定时任务,从各个数据库shard机器中非并行的备数据(也就是避免同时开始备份任务),当然也有热备份。每天晚上会扫描数据库集群生成快照(Snapshots),Flickr给出的经验中告诫,备份数据时,不要在删除(写入)大文件的时候立即进行写(删除)操作,这会损害性能。

图片是以文件的形式存储。一旦文件上传,应用服务会生成几种不同的size文件,完成之后,这些图片信息和图片的指向信息就被存进数据库。汇集数据也相当快,因为是在各自的shard上并行执行,10台shard并行响应速度可想而知。每个shard的最大连接数的是400个每秒,或者是设置成每个服务器+其相应的shard共 800个连接。最大45线程,因为目前还不会出现有超过45个用户同时并发的情况。

Tag标签

Tag是web2.0的特征,但tag不适合用传统的关系数据库来描述,Denormalization(反向规格化)和缓存是解决生成大量tag以控制在毫秒级的唯一方法 (Cal Henderson 的结论),为了实现毫秒级的大量tag生成,flickr的数据增加了一倍;还要写额外的程序去产生这些资料,而且还有额外的程序来保证数据间的一致性。可见高性能是和离复杂度分不开的。

他们的一些数据视图是后台离线计算的,把结果存储到MySQL中。因为其中有一些关系很复杂的运算,他们采取了利用服务器空闲的cpu来执行这些耗资源的程序。这可又是一复杂的处理。

另外,Flickr还实现了业务连贯性计划(Business Continuity Planning),以保证业务的不间断,让所有的数据都及时能写到数据层(db,memcache等),不会有服务挂起的情况。这个涉及到具体的业务流程了,但应该也属于构架很重要的一部分,但关于这方面的资料Flickr透露的还太少,可以因为这个太涉及业务流程了吧。

总结
我在这里只是总结了Flickr构架的整体概念,他们本身的发展历程就是一个很好的学习素材。我相信任何一个做web2.0的有志人士,都不会是想只停留在一个原始的无序阶段,无论您的模式怎样,最终都得有一个好的构架来支撑。好的构架就好比肥沃的土壤,业内各种乱飞的模式也只是一个个的种子而以。我曾听一个朋友提到过当年联众怎么没有独霸国内的在线小游戏市场的事,我想和他当年土壤很有关系吧。

      (图片来源于前面提到的资源中ppt截图)
21st
一月 2008

YouTube 的构架
爱因万江斯坦@2008年01月21日 21:38 Post in 系统构架 No Comments »

youtubeYouTube增长的十分迅猛,它成立于2005年2月,一年后的2006年3月,每天已经有3000万的视频服务请求,到了2006年7月,已经达到每天近1亿的视频访问,但负责整个网站维护的人数却并没有我们想象中的那么多。比较可信的说法是:2个系统管理员,2个软件构架工程师,2个开发工程师,2个网络工程师,1个DBA。

目前YouTube所使用的平台:
Apache
Python
Linux(Suse)
MySQL
psyco,一个动态的 python->C 编译器,用于为python提速
lighttpd,用于视频内容

YouTube使用NetScaler 进行负载均衡和缓存静态内容,Apache 使用 mod_fast_cgi,请求是由一个Python应用服务器来单独处理. 应用服务器请求多个数据库和其它的资源来获取数据,再把数据组装成一个Html页面。其构造极具可扩展:可以通过增加机器来扩充大规模需要的Web层。

系统中Python不构成瓶颈,大部分的时间是花费在远程调用上 (RPC),并且Python 适合快速灵活的开发和部署,这一点于市场竞争很关健。通常每个页面的服务时间小于 100 ms 。它还使用 psyco, 一个动态的 python->C 编译器 ,使用JIT编译器的方法,以优化内部循环。 在对CPU占用率比较高的应用,如加密应用,他们使用C来进行处理。为一些处理代价比较高的请求预先生成并缓存HTML。 在数据库中实现Row级别的缓存。缓存Python对象等。

YouTube的视频存储:

每个视频都存储在一个小型集群中,所以每个视频都至少存储在一个以上的机器上,更多的磁盘来存储内容意味着更快的速度。如果一台机器down掉,其它的机器可以补上,还可以做到联机备份.
因为Apache会有多的开销,所有服务器使用lighttpd做为视频文件的web server,使用 epoll 处理 fds.
比较受欢迎的视频会移到CDN上,点击量很少的内容(一天1-20个 views的那种)会存储在相应标识的服务器上.


YouTube的缩略图处理

对每个视频都有四张缩略图与之对应,这些缩略图都是专门由单独的服务器来提供web访问,这里就会遇到存储大量小文件所遇到的问题,文件系统也像是一个数据库,对于大文件到无所谓,但对于大量的零碎小文件,磁盘的I/O可受不了,何况每个目录所能存储的文件数也有一个极限,为此YouTube使用了Linux里的页面缓存和索引节点缓存,为了突破目录的存储文件数限制,在Ext3下,将文件转移到了多层次的目录结构上,目前在2.6内核下可以将Ext3的大目录处理能力提高100倍。但是,将大量文件存储在一个文件系统下仍然不是一个太好的方法。

一个页面显示60个缩略图,在每秒高请求的情况下,这样的高负荷Apache不太适合:在前端的Apache使用反向代理squid,刚开始还算凑合,但随着负荷的增加性能下降的很明显,从能处理每秒300个请求下滑到每秒20个。于是YouTube尝试用lighttpd,但lighttpd的单线程性能实在说不过去,又通过安装多线程的mod来让lighttpd支持高并发,但每个线程又有各位独立的缓存,就这导致安装一个存储大量图片的机器要消耗24小时,重启一次也要消耗6~10个小时以填充缓存。

为了解决这个问题,他们使用了Google的BigTable,一个分布的数据存储:避免了小文件存储的问题,速度够快,容错也不错,还有较低的延迟,因为它使用了一个分布式多级高速缓存,并能跨越不同的站点。
YouTube数据库

       早年:
使用MySQL存储元数据,像用户信息,tags和视频描述。
用一个包含10块硬盘的RAID 10 Volume盘阵存储数据,由于当时经济拮据,不得不租用硬件和使用信用卡购买硬件。他们也经历了从单一服务器到一个主服务器和多个副服务器的转变经历,然后数据库分区,再解决共享问题。这时遇到的问题是:主服务器是多线程大机器,它可以处理大量的任务,但副服务器是配置不太好的机器(我怀疑可能就是普通PC)只是用来单线程的工作,所以导致副服务器的响应严重滞后,因为缓慢的I/O导致了更新数据时的缓存丢失。使用replicating architecture复制构架,也只是用大量的money换来一点点的写入性能。他们的一个解决办法就是考虑数据的优先级,把数据分成两大类:一个是视频数据池,一个是通用数据群,这样的好处是让观看视频的应用得到更多的资源,而社会网络化这样的相对不太重要的功能能被路由到小容量的集群中。

现在:
实现了数据分区。分布式的读写,更好的本地缓存策略以减少IO,并减少了30%的硬件,避免了各服务器间的滞后,并且能方便的扩展数据库。

ok,从YouTube的发展和他的构架中我们可以看到,在最初的开始就应该为将来长远的发展考虑,从应用的特点和服务重点方面去考虑整个构架和性能的扩展成本。并不断的找出系统中的瓶颈,每一次瓶颈的解决,对于用户的体验就是一次增强,对于瓶颈,要有可控的预见和分析:软件,缓存,操作系统,磁盘I/O,数据库,带宽… …
YouTube成功的另一个要素,是他有一支学科交叉的团队,我在前面提到过他们的人员构成,各有专攻,一个良好的团队,有什么事是不可能的!:Impossible is nothing.