InfoQ

新闻

Twitter,架构的变迁

作者 Abel Avram 译者 黄璜 发布于 2009年6月30日 上午11时10分

社区
Architecture
主题
性能和可伸缩性
标签
缓存,
JVM,
C,
Ruby on Rails,
Scala

Evan WeaverTwitter服务团队的总工程师,他的主要工作是优化与伸缩性。在QCon London 2009上,他谈到了Twitter的架构,特别是在过去一年当中为提升Web站点性能所执行的优化。

Twitter使用的大部分工具都是开源的。其结构是用Rails作前端,C,Scala和Java组成中间的业务层,使用MySQL存储数据。所有的东西都保存在RAM里,而数据库只是用作备份。Rails前端处理展现,缓存组织,DB查询以及同步插入。这一前端主要由几部分客户服务粘合而成,大部分是C写的:MySQL客户端,Memcached客户端,一个JSON端,以及其它。

中间件使用了Memcached,Varnish用于页面缓存,一个用Scala写成的MQ,Kestrel和一个Comet服务器也正在规划之中,该服务器也是用Scala写成,当客户端想要跟踪大量的tweet时它就能派上用场。

Twitter是作为一个“内容管理平台而非消息管理平台”开始的,因此从一开始基于聚合读取的模型改变到现在的所有用户都需要更新最新tweet的消息模型,需要许许多多的优化。这一改动主要在于三个方面:缓存,MQ以及Memcached客户端。

缓存

每个tweet平均被126个用户跟踪,所以这里有着明显的缓存需求。在最初的配置中,只有API有着一个页面缓存,当每次从一个用户那里来了一个tweet时就会失效,而应用的其它部分都是无缓存的:

image

第一个架构改动是创建一个直写式向量缓存包含了一个tweet ID的数组,tweet ID是序列化的64位整数。这一缓存的命中率是99%。

第二个改动是加入另一个直写式行缓存,它包含了数据库记录:用户和tweets。这一缓存有着95%的命中率并且使用了Nick Kallen的名为Cache Money的Rails插件。Nick是Twitter的一名系统架构师。

第三个改动是引入了一个直读式的碎片缓存,它包含了通过API客户端访问到的tweets的序列化版本,这些tweets可以被打包成JSON,XML或者是Atom的格式,有着同样是95%的命中率。这一碎片缓存“直接消费向量,而且如果现在缓存了一个序列化的碎片,它不会加载你试图看到的该tweet的实际的行,因此它将在大量时间将数据库置于短路状态,”Evan这样说到。

还有另一个改动是为页面缓存创建一个单独的缓存池。根据Evan的说法,该页面缓存池使用了一个分代的键模式,而不是直接的失效,因为用户可以

发送HTTP的if-modified-since并且将任何他们想要的时间戳放入请求路径,我们需要将这一数组切片并只呈现给他们他们想要看到的tweets,但我们不想跟踪客户端所使用的所有可能的键值。这一分代的键模式有一个大问题,在于它不会删除所有失效的键值。每一个被加入的对应到人们所接收的tweets数目的页面都会向缓存推送有效的数据,最后变得我们的缓存仅仅只有五个小时的有效生命周期,因为所有的页面缓存都将流过。

当该页面缓存转移到其自己的池之后,缓存未命中降低了将近50%。

这是Twitter现在所使用的缓存模式:

image

因为80%的Twitter流量都来自API,因此还有额外的二层缓存,每一个最多将处理95%来自前一层的请求。整体的缓存改动总共有百分之二三十的优化,它带来了

10倍的容量提升,它本可以更多,但现在我们遇到了另一瓶颈...我们的策略是首先加入直读式缓存,确保它正确失效,然后再转移到直写式缓存并且在线修复,而不是当一个新的tweet ID进来时每次都要销毁。

消息队列

因为,平均来说一个用户有126个追随者,这就意味着每个tweet将有126个消息在队列里。同时,流量会有出现高峰的时候,就像在奥巴马就职的时候达到了每秒几百个tweet或者说是成千上万的消息在队列里,是正常流量的3倍。MQ应当去化解这一高峰并随着时间将其分散,这样就不用增加许多额外的硬件。Twitter的MQ很简单:基于Memcached的协议,job之间是无序的,服务器之间没有共享的状态,所有的东西都保存在RAM里,并且是事务性的。

第一版的MQ实现是用的Starling,以Ruby写成,伸缩性不佳,特别是Ruby的GC不是分代的。这将导致MQ在某一点上崩溃,因为GC完成工作时将会把整个队列处理中止。因此作出了将MQ移植到Scala上的决定,它有着更为成熟的JVM GC机制。现有的MQ仅仅只有1200行代码并且运行在3台服务器上。

Memcached客户端

Memcached客户端的优化目的是试图优化集群负载。现在的客户端用的是libmemcached,Twitter是其最重要的用户和其代码库最重要的贡献者。基于此,持续一年的碎片缓存优化带来了50倍的每秒页面请求服务增加。

image

因为请求来自的位置难以确定,处理请求最快的办法就是将预先计算好的数据存储在网络RAM上,而不是当需要的时候在每个服务器上都重新计算一次。这一方式被主流的Web 2.0站点所使用,它们几乎都是完全直接运行于内存之上。根据Evan的说法,下一步就是“既可伸缩的读持续了一年之后,(解决)可伸缩的写,然后就是多协同定位的问题”。

这一QCon的演示文件发布在Evan的站点上

查看英文原文:Twitter, an Evolving Architecture

关键性的技术点是所有实现是开源的。 发表人 deshi xiao 发表于 2009年6月30日 下午8时48分
MQ只有1200行代码 发表人 Jeffrey Zhao 发表于 2009年6月30日 下午10时49分
关键在缓存 发表人 Roy Wang 发表于 2009年7月5日 下午9时35分
  1. 返回顶部

    关键性的技术点是所有实现是开源的。

    2009年6月30日 下午8时48分 发表人 deshi xiao

    如果你想应用它的技术,可以查找英文的资料,github上有热腾腾的代码。

  2. 返回顶部

    MQ只有1200行代码

    2009年6月30日 下午10时49分 发表人 Jeffrey Zhao

    看来只是一个非常非常轻量的解决方案了……关键还是在缓存方面,但是没看懂……

  3. 返回顶部

    关键在缓存

    2009年7月5日 下午9时35分 发表人 Roy Wang

    这一方式被主流的Web 2.0站点所使用,它们几乎都是完全直接运行于内存之上。
    这句话是精髓。硬件方面的进步必然带来软件技术的革新。如何更好的利用RAM,将是web架构进化的一个方向。

深度内容

模块化Java:声明式模块化

本文是模块化Java系列文章的第4篇,介绍的是声明式模块化。文中描述了组件如何以声明的方式来定义并组织在一起,而无需让代码依赖于OSGI API。

Ian Robinson和Jim Webber谈论基于Web的整合

本采访是在伦敦举行的QCon2009上记录的,Ian Robinson和Jim Webber探讨了如何将Web作为整合平台以及REST在理论上和实践中的好处。

项目管理修炼之道(精选版)

项目管理对于项目成败至关重要,但实践中每个项目都有自己的独特性,没有现成的解决方案可以套用。书中从应对实际风险的角度出发,讲述了从项目启动、项目规划到项目结束的整个管理流程,展示了作者的思考过程。本迷你书从原书中精选出5个章节。

那是鸟,还是飞机?不,那是超人!

在这个演讲中,Fred将会揭示敏捷的一些外在因素,并会重点关注敏捷获得成功的内在原因。从案例研究和真实的项目经验来看,Fred认为:工具、管理体系都不能让你变得敏捷。敏捷的成功,植根于士气高涨、充分授权的工作者身上,他们能够以不同以往的方式思考问题。

访谈和书摘:Eben Hewitt的新书《Java SOA Cookbook》

Java SOA Cookbook

Eben Hewitt的新书《Java SOA Cookbook》从Java实现的角度讨论了面向服务架构。Eben在书中讨论了SOA基础、工具、最佳实践和SOA治理等主题。

Mark Richard的《Java消息服务》第二版

Mark Richards的新书《Java消息服务》第二版覆盖了JMS的许多主题, 包括发布和订阅模式以及点对点模式,消息过滤和事务等。InfoQ与Mark谈论了跟他的新作。

模块化Java:动态模块化

本文是“模块化Java”系列文章的第三篇,讨论动态模块化,内容涉及如何解析bundle类、bundle如何变化、以及bundle之间如何通信。

让测试也敏捷起来

对于测试组织来说,敏捷方法带来的快速迭代却让测试本身变得困难起来:缺乏“足够详细的文档”,缺乏“仔细设计用例的时间”等等。在本演讲中,段念将与大家探讨如何在敏捷过程中进行测试。