51CTO推荐:视频专题-大型网站架构技术专家谈
Evan Weaver是Twitter服务团队的总工程师,他的主要工作是优化与伸缩性。在一个技术峰会上,他谈到了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时就会失效,而应用的其它部分都是无缓存的:
第一个架构改动是创建一个直写式向量缓存包含了一个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现在所使用的缓存模式:
因为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倍的每秒页面请求服务增加。
因为请求来自的位置难以确定,处理请求最快的办法就是将预先计算好的数据存储在网络RAM上,而不是当需要的时候在每个服务器上都重新计算一次。这一方式被主流的Web 2.0站点所使用,它们几乎都是完全直接运行于内存之上。根据Evan的说法,下一步就是“既可伸缩的读持续了一年之后,(解决)可伸缩的写,然后就是多协同定位的问题”。
【编辑推荐】