随着每月页面浏览量突破150亿次,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亿次页面浏览量
- 每月150亿次以上页面浏览量
- 约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流直接从传输线上阅读内容。不使用MySQL的原因在于,分区工作本身就过于频繁,因此不适合让其承担大量数据流。
Tumblr的传输线模式灵活性极强,Twitter所使用的传输线则相形见绌,其中的数据常常面临丢失。
传输线流能够及时进行回溯,它会保存一周以内的所有数据。在连接时,它可以正确找回最后一次阅读的位置。
允许同时连入多个客户端,而且每个客户端所看到的内容都各不相同。每个客户端拥有独立的客户端ID,这要归功于Kafka所支持的用户组概念。用户组中的每一个用户不仅会看到与自己相关的消息,并且消息内容绝不重复。可以利用同一个用户ID创建多个客户端,这些客户端所看到的内容同样不会重复。也就是说数据以独立且并行的方式进行处理。Kafka通过ZooKeeper定期为用户设置检查点,以记录用户的当前阅读进度。
Dashboard收件箱的Cell设计
目前为Dashboard功能提供支持的分散-集中模式存在着一定局限性,不久之后就会转而采用更先进的方案。
解决方案是让收件箱采用以Cell为基础的新型架构,这与Facebook的Messages功能颇为相似。
收件箱可以说是分散-集中模式的对立面。由关注者的博文及近期动态所构成的Dashboard版面,按时间顺序及逻辑方式存储在一起。
由于收件箱的功能特性,分散-集中模式带来的问题在这里不攻自破。我们只会询问收件箱中保存着哪些内容,这完全不涉及关注者、用户好友之类乱七八糟的关系。这样的情况将持续很长一段时间。
对Dashboard内容进行重写是相当困难的。虽然数据拥有分布式特性,但它同时也拥有交互式特性,因此用户无法实现局部更新功能。
数据总量之大简直超乎想象。每一条消息平均要发送给成百上千位不同用户,这比Facebook的工作负担更重。大量数据加上高分布率再加上多个数据中心协同运作,整个流程的复杂程度由此可见一斑。
每秒百万次写入操作及五万次读取操作,这么高强度的交互活动带来了高达2.7TB的数据集增长量,还不算复制或是压缩等处理工作。由24个字节构成的百万次写入操作给收件箱带来海量信息。
以上所有工作由一套应用程序负责,因此保证程序的正常运作就显得至关重要。
Cell单元。
每个Cell单元都是一套独立的装置,其中包含了大量用户的全部相关数据。用户Dashboard版面所需要的一切数据都由Cell单元提供。
用户以映射的方式与Cell单元连通,每个数据中心都运行着大量Cell单元。
每个Cell单元都拥有自己的一套HBase集群、服务集群以及Redis缓存集群。
用户与Cell单元相对应,所有Cell单元都通过传输线中的上传数据调用博客信息。
每个Cell单元都以Finagle为基础,并通过传输线以及Thrift上的服务请求构成HBase数据信息。
当某位用户进入Dashboard版面,就会从特定Cell单元中调出多个相关用户的信息;服务器节点通过HBase读取相关用户的Dashboard内容并将数据传回当前登录用户。
后台任务从传输线中获取信息,并生成对应列表及处理请求。
Cell单元中的博客信息会调用Redis缓存层。
请求指令流:当某位用户发布博文,该篇博文即会被写入传输线;所有Cell单元从中读取博文内容并将其转写入文章数据库中;接着Cell单元会检查该文用户的关注者中有哪些处于自身单元内部,最终将寻获的关注者收件箱以及博文ID共同进行上传。
Cell单元设计的优势所在:
请求指令的大量同时出现势必对并行处理能力要求极高,这就使得设备组件必须彼此独立,然而独立性也会导致交互过程无法完成。Cell单元设计使并行处理工作成为一个独立体系内部的事务,并能够在用户群体不断增长的情况下随时保持强大的可调节能力。
Cell单元天然具备故障隔离能力。某个Cell单元出错不会对其它单元造成任何严重影响。
Cell单元使更新内容测试、信息发布以及软件版本测试工作得以有条不紊地进行。
容易被大家忽略的关键性环节是:所有文章都会被复制到每一个Cell单元中去。
每个Cell单元都存储着一套独立的全局文章副本。每个Cell单元都能够为Dashboard版面提供令人满意的内容。应用程序并不会查询所有文章ID,它只会为登录ID查询相关文章,这样一来用户将能够在自己的Dashboard版面中看到所有信息。每一个Cell单元都拥有填充Dashboard所必需的全部数据,因此不必进行什么跨单元通讯。
共使用了两套HBase列表:一套用于存储文章副本,而另一套则为Cell单元中的每个用户保存博文ID,列表之间的内容基本无甚关联。第二套列表的作用是为用户的Dashboard版面提供显示内容,也就是说不必追踪该用户所关注的每位其它用户。另外,利用这种机制,如果我们在另一台设备上阅读或浏览某篇文章,系统将不会认为我们是在重复读取同样的内容。无论通过何种方式,我们的阅读进度都将保存在收件箱模块的状态一项中。
文章不会被直接置于收件箱中,因为这么多文章累积起来容量实在惊人。因此只将ID放入收件箱,而文章内容则由Cell单元保存。这种方式大幅度降低了所需存储空间,并让用户收件箱中的内容按时间顺序变得非常简便。这么做的缺点是每个Cell单元都需要包含完整的文章副本。但令人惊讶的是,文章本身的体积总量远远小于映射所需的存储空间。每天每个Cell单元中的文章内容增量约为50GB,但收件箱内容的增量却高达每天2.7TB。由此可见,用户的阅读量比写入量大得多。
用户的Dashboard中并不显示文章内容,只包含基本的文章ID;因此数据增量的主体是不断膨胀的ID。
即使用户的关注者名单发生变更,这种设计仍然完全应付得来,因为所有文章都已经存在于Cell单元中。如果Cell单元中只保存来自关注者的文章,那么一旦关注者名单有所变动,单元中的数据将不足以及时做出反应,同时将引发一系列不可避免的信息填充活动。
另有一套备选方案,利用一套独立的文章集群存储博文内容。这种设计的缺点在于,一旦这套集群出现故障,那么整个网站都将陷入瘫痪。相比之下,采用Cell单元设计、将所有文章复制到每一个单元当中的做法使整个架构牢固而可靠。
对于那些关注人数以百万计且相当活跃的用户,会根据他们的访问模式获得特定的用户服务。
不同的用户往往使用不同的访问模式以及与个人习惯相符的分布模式。Tumblr为用户准备了两种不同的分布模式:一种适合人气超高的热门用户、另一种则适合任何类型的用户。
数据的处理方式也根据用户类型的变化而有所不同。来自活跃用户的文章实际上不会真正予以发布,而只是有选择地呈现给其他用户。
Tumblr在处理大量关注其他使用者的用户时,采用的方法与处理拥有大量关注者的用户非常类似。
每个Cell单元的大小很难确定。单元的大小会影响到网站的故障机率,而单元中所包含用户的数量则是最关键的因素。需要在用户的使用期望与支撑这种期望的成本支出之间找到一个平衡点。
从传输线中读取信息是网络问题的核心内容。在Cell单元内部,网络流量始终处于可管理的状态。
由于Cell单元的数量日益增加,最终Cell单元组的概念被添加进来。这是一套分层复制规划,同时也有利于将数据信息迁移到多个数据中心当中。
成为一颗立足于纽约的耀眼新星
- 纽约的环境背景相当特殊,其中充斥着大量财务与广告因素。作为一家缺乏经验的新兴企业,Tumblr在人才招聘及设备租用方面都面临着挑战。
- 在过去几年中,纽约市一直致力于扶植新兴企业。纽约大学与哥伦比亚大学制订了完善的计划,鼓励在校学生到新兴企业中去实习,而不是一头扎进华尔街。Bloomberg市长也将支持科技发展作为纽约市的一项基本战略。
团队构成
- 团队:基础设施、平台、SRE(即系统调整及修复)、产品、网络运行以及服务支持。
- 基础设施:5层及以下。IP地址及以下、DNS、硬件供应。
- 平台:核心应用程序开发、SQL分区、服务、网络运行。
- SRE: 存在于服务团队与网络运行团队之间,致力于解决迫在眉睫的系统可靠性及可扩展性问题。
- 服务团队:致力于发展战略层面的问题,处理周期一般为一到两个月。
- 网络运行:负责问题检测与响应以及相关调整工作。
软件开发
- 一切工作以一套专门将PHP应用程序分布到各处的rsync脚本集合为基础。一旦设备总数达到200台,系统就会发生故障——部署流程无比缓慢、设备自身也停滞在各种各样的配置状态中。
- 在此之后,Tumblr决定将部署流程(包括开发、启用及生产等环节)利用Capistrano创建在服务堆栈中。这在数十台设备协作的情况下没有问题,但通过SSH让数百台设备彼此连接后,问题再一次出现。
- 现在所有设备上都运行着一套调整软件,这套软件以红帽Func为基础,本质上是一个用于将指令分配到各主机处的轻量型API。规模化要素也被部署在Func内部。
- 开发工作在Func层面上进行,并采用“在某个主机组上执行某操作”的直观表述,这就成功回避了对SSH的依赖。例如我们希望在A组中部署软件,那么管理员只需选定目标节点集合并运行部署指令,即可完成任务。
- 部署指令通过Capistrano进行实施,并能够以git检查或者仓库输出的形式生效。扩展性方面也有所保障,因为指令面向的交互对象为HTTP。之所以采用Capistrano,是因为它能在良好的版本控制之下支持简单目录,这种特性使其与PHP应用程序之间的配合更为默契。以版本控制更新活动使得每个目录都包含安全散列算法,这样技术人员也能够方便地核对当前版本是否正确。
- Func API的作用是回馈状态,通知系统哪些设备正在运行哪些版本的软件。
- 任何服务项目都能够安全地加以重启,因为默认状态下项目将首先逐渐断开连接、之后才实施重启动作。
- 每项功能在投付使用之前,都会在独立环境下进行测试。
开发工作
- Tumblr所秉承的理论是让每位用户都能使用自己想要的工具,但随着用户群体的激增,这种想法实在难以为继。招聘精明强干的技术人员不是件容易的事,因此他们开始对堆栈进行标准化改造,旨在让技术工作更好上手、团队培养更加高效、产品问题的处理更加迅速并在上述内容的基础上建立业务运作。
- 整个开发过程与Scrum比较相似。选择的是工作量较小的轻量级模式。
- 每位开发人员都拥有一台经过预先配置的开发专用设备。设备更新工作则通过Puppet实现。
- 开发用设备可以随时回收并进行变更及测试,接着重新投入使用并最终执行生产任务。
- 开发人员们常用的文本编辑器是Vim和Textmate。
- PHP应用程序的测试以代码审核的方式进行。
- 他们在服务项目端部署了一套测试用基础设施,其中包括hooks、Jenkins以及连续性集成与通行创建系统。
招聘流程
- 面试阶段一般不会涉及数学、分析以及脑力测验。提出的问题主要围绕着求职者所申请的职位展开。他们是否聪慧?能否顺利完成份内的工作?但评估求职者能否“顺利完成份内工作”有一定难度。总之面试的目的是找出身负才能的新人,而不是有意排除一定比例的求职者。
- 编码能力是考核的一大重点。他们会针对示例代码提出问题,在电话面试的过程中也常常利用Collabedit考验求职者的共享代码编写能力。
- 面试本身并不具有对抗性,他们只是在尝试发现优秀的人才。求职者们可以在面试中使用像谷歌这样的辅助工具来解决问题,因为Tumblr相信善于使用外界助力的开发人员才是能够独当一面的上上之选,所以在考核中并不禁止使用辅助手段。
鉴于Tumblr所要应对的数据流量相当巨大,因此招徕在规模化业务方面具备丰富经验的人才正是当务之急。事实上在全球范围内,还没有几家企业需要处理如此规模的日常工作。
举例来说,他们需要一款新的ID生成器,要求服务运行环境为JVM,在每秒一万条请求的使用频率下保证响应时间低于1毫秒,并且要在内存限定为500MB的前提下保证高可用性。结果在招聘过程中,他们发现了几位牛人,不仅严格按照给定条件执行、还将响应延迟降低到新的水平。这也最终使得Tumblr愿意花费大量时间对JVM运行环境进行调整。
在Tumblr的工程博客中,他们专门发表文章对Dennis Ritchie与John McCarthy表示敬意及纪念,这正是公司极客氛围的实际体现。
经验教训
- 自动化流程无处不在。
- MySQL(与分区机制)具备可扩展性,而应用程序本身则不行。
- Redis功能惊人。
- Scala应用程序的性能表现令人赞叹。
- 在不确定某个项目能否起效时,果断加以废除。
- 不要过分在意那些在垃圾技术领域残喘多年积累到的所谓经验。真正重要的是他们是否适合自己的团队、是否能胜任新的工作岗位。
- 选择一套能帮你雇用到所需人才的招聘机制。
- 根据技术团队的实际状况定制招聘标准。
- 尽量多读些技术论文以及博客文章。像Cell单元架构以及选择性呈现这样的好点子往往并非原创。
- 多与同事们交流。他们中肯定有人会与来自Facebook、Twitter、LinkedIn等网站的工程师们探讨工作经历并学习新知识。我们自己可能还没有达到同样的高度,但处处留心绝对有利无弊。
- 另外,别让新人直接负责技术工作。在接触实际生产流程之前,他们需要在试验性项目组或者工作压力较小的职位上好好拿出时间学习HBase以及Redis。
在这里我要向Blake表示感谢,也正是由于他的耐心配合,这篇汇总型文章才会与大家见面。采访过程中他表现出令人称道的慷慨心态,并且不厌其烦地向我们解释工作流程中的具体细节。
原文:Tumblr Architecture - 15 Billion Page Views a Month and Harder to Scale than Twitter