月PV破15亿:Tumblr架构揭密

随着每月页面浏览量突破15亿次,Tumblr已经名正言顺地跻身博客类平台中的名人堂。用户们对它的简洁、美观以及对使用体验的专注追求赞不绝口;它的相关社区也同样氛围温馨、人气爆棚。总之,人们喜欢这位博客家族中的新贵。

 

超过30%的月度增长不可能一帆风顺,过程中的坎坷与挑战也自然不言而喻,但最令人头痛的还是可靠性问题。正是经过技术人员的不懈努力,Tumblr才取得了如此惊人的规模及傲人的运行成绩:每天5亿次页面浏览、每秒四万次查询请求峰值、每天新数据存储量高达约3TB、总支持服务器达到1000余台。

大多数成功的新兴企业都面临着相似的困扰,由于从初来乍到的新成员一跃成为万众瞩目的焦点角色,一道巨大的危险鸿沟硬生生将原本孱弱的技术支持团队推向了风口浪尖。招聘员工、扩大基础设施、维护旧有基础设施的同时,仍然只有寥寥数位工程师负责着每个月巨大的数据吞吐量。这样的工作强度意味着我们在处理过程中必须有所侧重,而做出这样的选择无疑相当艰难。Tumblr目前也陷入了这样的困境,他们的做法是创建起一个由二十位精力充沛的工程师组成的技术小组,在应对日常事务的同时制订出多种极富创意的解决方案。

Tumblr创立之初就将自己定位为典型的大型LAMP(即由定制组件构成的系统)应用程序。如今他们前进的方向是通过Scala、HBase、Redis、Kafka、Finagle以及以架构为基础的新颖单元共同打造出多种分布式服务模块,借以构成完整的Dashboard导航内容。目前他们已经开始尝试修复PHP应用程序中的短期问题、将故障部分抽离出来并利用服务机制进行更正。

Tumblr发展的主题是在庞大的平台规模之上实施成功转型。即将LAMP堆栈转变为一套更具突破性特性的新堆栈,同时将原本适用于新兴企业的小型团队扩展为装备精良、擅长开发工作的成熟团队,进而为用户带来大量新功能及基础设施。为了帮助我们更透彻地理解Tumblr实现这一方针的具体做法,Tumblr公司分布式系统工程师Blake Matheny分享了他的心得体会。以下是Blake对Tumblr做出的信息汇总:

Tumblr官方网址:http://www.tumblr.com/

 

统计

  • 每天5亿次页面浏览量
  • 每月15亿次以上页面浏览量
  • 约20位技术工程师
  • 每秒4万次查询请求峰值
  • Hadoop集群每天接受超过1TB的数据量
  • MySQL/HBase/Redis/Memcache等系统每天需要处理数以TB计的信息
  • 每月用户数量及处理负载增长30%
  • 日常运行中涉及约1000个硬件节点
  • 每个月每位工程师平均要面对上亿次页面访问活动
  • 每天新增的博文及帖子约为50GB。新的用户关注列表每天带来约2.7TB的数据存储量
  • Dashboard系统每秒要应对百万次写入操作、5万次读取操作,这一数字还在不断增长之中

软件

  • 开发工作在OS X上进行,日常运行则交给Linux系统(CentOS、Scientific)
  • Apache
  • PHP, Scala, Ruby
  • Redis, HBase, MySQL
  • Varnish, HA-Proxy, nginx,
  • Memcache, Gearman, Kafka, Kestrel, Finagle
  • Thrift, HTTP
  • Func - 一款安全且脚本化良好的远程控制框架及API
  • Git, Capistrano, Puppet, Jenkins

硬件

  • 500 台web服务器
  • 200台数据库服务器(其中大多数作为故障发生时的后备资源存在)
    • 47个资源池
    • 30个区块
  • 30台缓存服务器
  • 22 台redis服务器
  • 15 台varnish服务器
  • 25个haproxy节点
  • 8 个nginx反向代理服务器
  • 14台作业队列服务器(kestrel+gearman)

架构

  • 比起其它社交型网络,Tumblr拥有一套完全不同的使用模式。
    • 每天有五千多万篇博文发表,每一篇都有数百次的平均浏览量。并不是说少数热门用户具备的数百万关注者拉升了平均浏览量,事实上每位Tumblr用户基本都有成百上千的关注者。这与以往任何类型的社交网络都有所不同,也恰恰是这种特性让Tumblr面临着史无前例的规模化考验。
    • Tumblr目前是用户倾注时间第二多的热门社交网络,其内容也极具吸引力。由于允许用户自由分享图片与视频,因此博文的体积不再以字节计算。尽管用户不一定总会发布体积庞大的内容,但网站保证每个人都在需要时具备丰富自己文章的能力。人们乐于撰写更为深刻的主题,这也让关注者们找到了阅读的乐趣,并持续花费大量时间访问Tumblr。
    • 用户之间构成一套完整的联络纽带,因此他们常常会尝试回溯Dashboard中数百页之前的内容。其它社交网络则往往只提供信息流,用户体验只停留在惊鸿一瞥的层面上。
    • 也就是说,由于用户数量之大、每位用户的平均阅读数量之多以及用户发起各类活动积极性之高,Tumblr需要处理的上传信息庞大到令人惊愕。
  • Tumblr运行于一套托管站点中。设计者们为网站留出了充分的地理分布空间,以应对未来的发展需求。
  • Tumblr平台由两大组件构成:Public Tumblelog与Dashboard系统。
    • Public Tumblelog 是一款面向公众的博客。由于动态特性较弱,因此缓存更易于打理。
    • Dashboard在功能上与Twitter的timeline颇为相近。用户将实时接收到自己关注对象的更新信息。
      • 在规模特性上与其它博客载体颇为不同。缓存在这里的作用不再明显,因为每条请求都各不相同,尤其是对于那些活跃的关注者而言。
      • 运行中必须保持高度的实时性与一致性,不应显示陈旧数据,且需要处理的数据量非常庞大。每天的博文内容本身只有50GB,但关注者列表更新信息则高达2.7TB。媒体文件全部存储于内存中。
  • 大多数用户将Tumblr作为内容型消费工具。每天页面浏览量超过5亿次,70%的浏览行为针对Dashboard系统。
  • Dashboard系统的可用性相当值得称道。Tumblelog则一直有所欠缺,因为其中的某套原有基础设施目前很难实施迁移。鉴于技术支持团队的规模太小,他们不得不在规模化进程中优先处理那些时间短、见效快的内容。

过去的Tumblr

  • Tumblr起初立足于Rackspace公司,后者为每个自定义域名博客创建一套A记录。到了2007年,他们已经拥有极为庞大的用户群体,为了实施整体迁移,他们开始尝试独立于Rackspace公司之外。这时他们的自定义域业务仍然由Rackspace负责,为了保持用户的访问方式,他们利用HAProxy与Varnish代理服务器将Rackspace域名转向自己的服务器空间。类似的历史遗留问题还有很多。
  • 一套传统的LAMP。
    • 一直使用PHP,几乎每位工程师都通过PHP处理编程工作。
    • 创立之初只拥有一台web服务器、一台数据库服务器以及一套PHP应用程序。
    • 为了完成规模化扩展,他们开始使用memcache,接着引入前端缓存,然后是将HAProxy置于缓存之前,最后采用MySQL分区。MySQL分区体系的加入使整体服务效果迈上新的台阶。
    • 力图将所有处理负载控制在一台服务器的承受能力之内。在过去的一年中,他们借助C语言开发出两款后端服务:ID生成器与Staircar,Dashboard通知功能则利用Redis实现。
  • Dashboard采用分散-集中式处理方案,当用户访问Dashboard时事件将自动予以显示。而让用户关注对象的新事件以推送形式显示又花了技术团队六个月的时间。由于数据以时间为标准排序,因此分区设计方案的表现并不尽如人意。

如今的Tumblr

  • 出于提高租用及开发速度的考量,如今采用JVM中央方案。
  • 目标是将所有存在于PHP应用中的内容迁移至服务项目中,并将PHP应用本身作为新建的层置于服务之上,用于负责请求验证及介绍说明等工作。
  • 选择使用Scala与Finagle。
    • 公司内部拥有大量擅长Ruby及PHP开发工作的成员,因此Scala的推广也就更为轻松。
    • Finagle的使用也是他们选择Scala的重要原因之一。这是一套来自Twitter的库,能够处理大多数由分布式设施带来的技术难题,包括分布式跟踪、服务项目搜索以及服务项目注册等。当然这些功能我们也不必全部采用,根据实际需求选择即可,反正都是免费的。
    • 一旦与JVM环境相结合,Finagle能够提供他们所需要的一切基本要素(例如Thrift、ZooKeeper等等)。
    • Foursquare及Twitter一直在使用Finagle,Scala也始终效力于Meetup网站。
    • 与Thrift应用程序接口一样,Finagle也带来了相当优异的性能表现。
    • 希望得到像Netty那样的运行效果,又不想涉及Java,因此Scala是最好的选择。
    • 采用Finagle是因为它功能强劲、所需知识并不冷门,使用中不涉及过多网络代码而且能为分布式系统提供一切所需功能。
    • Node.js没有入选的原因在于,它在JVM环境下对于技术团队的规模要求比较高。Node.js在开发方面缺乏标准化及最佳实践选项,测试用代码也相对较少。而Scala允许我们使用任何现有Java代码。而且在不要求过多知识储备的基础上,Scala能够轻松应对未来可能出现的扩展性要求,同时保障5毫秒响应时间、49秒双机集群切换以及平均每秒四万次、峰值四十万次的请求处理。同时Java体系中的大量资源也可为我们所用。
  • 内部服务项目正由以C语言/libevent函式库为基础向Scala/Finagle为基础转变。
  • 采用更新的HBase以及Redis等非关系类数据存储机制,但目前大多数数据存储在一套高度分区下的MySQL架构中。尚没有用HBase彻底替代MySQL。
  • HBase凭借数以亿计的URL以及全部历史数据与分析结果支持自身的URL简写功能,运行表现一直稳固可靠。HBase主要用于处理高写入请求情况,例如每秒上百万次写入动作的Dashboard重置操作。HBase没有被用来替代MySQL的原因在于,Tumblr目前的技术支持团队尚无法在HBase上完成全部业务需求,因此他们最终决定先在规模较小的非关键性项目上进行试用,尽量积累使用经验。
  • 以MySQL及分区机制为基础,将按时间排序的所有数据纳入同一个分区的过程始终麻烦不断。读取及复制操作也由于子集群写入动作的同时发生而常常出现滞后现象。
  • 创建出一套通用型服务框架。
    • 事先花费大量时间制订方案,用以解决分布式系统在管理时出现的操作问题。
    • 创建一套用于服务项目的Rails桥架,作为引导内部服务的模板使用。
    • 所有服务项目从运行的角度来看都完全一致。各服务的状态检查、监控、启用以及中止都以同样的方式进行。
    • 创建过程中使用的工具为SBT(一款Scala创建工具),同时用到的还有其它一些插件及辅助内容,旨在为包括git项目标注、仓库信息发布等公共活动提供支持。大多数开发人员不必深入了解系统的创建过程。
  • 前端层使用HAProxy,Varnish则服务于公共博客,二者共计占用40台设备。
  • Apache及各类PHP应用程序由500台网络服务器予以支持。
  • 数据库服务器共有200台。大部分数据库服务器用于提高整体可用性。由于使用标准化硬件配置,因此平均无故障工作时间相当令人满意。硬件设备的损耗大大超出预期,因此技术人员准备了大量后备物资以应对不时之需。
  • 六项用于支持PHP应用程序的后端服务。一个专项小组专门负责开发此类后端服务。每隔两到三周都会有一款新服务推出,例如Dashboard通知、Dashboard辅助索引、URL简写工具以及用于分区处理的缓存代理器等。
  • 在MySQL分区方面投入大量时间以及人力物力。尽管MongoDB目前在纽约(Tumblr的总部所在地)风靡一时,但他们仍然采用了扩展性更好的MySQL。
  • Gearman,一款作业队列系统,被用于处理运行时间较长且无人照看的工作内容。
  • 可用性评估以实际使用效果为标准。用户能够正常访问自定义域名或是Dashboard吗?另外故障率也是评估中的一项因素。
  • 就长期运行状况来看,具备最高优先级的项目必须首先得到修复。目前故障模式已经得到有效的分析与系统性解决,其目的是同时从用户及应用程序的角度来评估整体运行状态。如果一条请求中的某部分没有得到充分响应,那么这套评估机制必须迅速查明原因。
  • 最初的角色模式是由Finagle负责支持的,但后来这种方式被弃之不用,取而代之的是一套无需照看的作业队列。另外,Twitter的实用库中包含一套名为Futures的服务实施方案。当某个线程池需要被调用时,Futures服务将立即建立一个future池,这时所有待处理内容都将被提交至future池中以进行异步执行。
  • 由于自身特性的原因,Scala并不适合处理共享状态。Finagle则可以默认为适合处理此类情况,因为Twitter已经将付诸实际应用。在Scala及Finagle中都应尽量避免可变状态调用架构的情况。不能让设备长期处于运行状态。状态信息采集自数据库,并在使用后重新写入数据库。这么做的优势是,开发人员们不必担心线程或者锁定问题。
  • 22台Redis服务器。每台服务器运行8到32个实例,也就是说共计有数百个Redis实例用于生产流程。
    • 后端存储机制用于为Dashboard通知功能提供支持。
    • 通知功能本身类似于关注我们博文的某位用户。通知会显示在用户的Dashboard中,这样我们就能及时了解其它用户的最新动态。
    • 极高的写入频率使得MySQL有些力不从心。
    • 通知内容无需长期存在,因此即使用户掉线也不会出现突然涌入大量提示消息的窘境,这样一来Redis就成为实现通知功能的选择之一。
    • 尽量为技术人员创造机会,让他们学习Redis的相关知识并熟悉其工作原理。
    • 由于Redis拥有强大的社区体系,因此无论遇上什么问题都能在这里找到解决方法。
    • 为Redis创建一个以Scala futures为基础的接口,这项功能现在正逐渐转到Cell架构当中。
    • URL简写工具将Redis作为一级缓存,并把HBase当成长效存储机制。
    • Dashboard的辅助索引围绕Redis进行创建。
    • Redis作为Gearman的持久化层存在,并用到了由Finagle创建的缓存代理器。
    • 慢慢由缓存向Redis迁移。希望能够以一套快取服务作为最终方案,其性能表现应与缓存方案一致。

内部传输线

内部应用程序需要访问活动流。每个活动流的内容都与用户行为相关,例如创建、删除博文或者喜欢、反感某些博文等。挑战在于如何将这样规模的数据实时加以分散。要达到这一目的,需要足以支持大规模内部扩展的工具以及拥有可靠生态系统辅助的应用程序。此外还要确立分布式系统的中心点。

  • 之前信息的分布化由Scribe/Hadoop负责。服务项目要首先登入Scribe并开始追踪,然后将数据传输至应用程序端。这种模式让可扩展性成为空谈,尤其是在用户每秒创建上千篇博文的峰值时段。应该尽量避免用户追踪文件并进行打印。
  • 创建一套类似于信息公交车这样的内部传输线,服务项目与应用程序通过Thrift与传输线进行交互。
  • 利用来自LinkedIn网站的Kafka来存储消息。内部用户使用HTTP
     

 

你可能感兴趣的:(揭密,月PV破15亿,Tumblr架构)