月PV破150億:Tumblr架構(gòu)揭密
譯文【51CTO精選譯文】隨著每月頁面瀏覽量突破150億次,Tumblr已經(jīng)名正言順地躋身博客類平臺中的名人堂。用戶們對它的簡潔、美觀以及對使用體驗(yàn)的專注追求贊不絕口;它的相關(guān)社區(qū)也同樣氛圍溫馨、人氣爆棚??傊?,人們喜歡這位博客家族中的新貴。
超過30%的月度增長不可能一帆風(fēng)順,過程中的坎坷與挑戰(zhàn)也自然不言而喻,但最令人頭痛的還是可靠性問題。正是經(jīng)過技術(shù)人員的不懈努力,Tumblr才取得了如此驚人的規(guī)模及傲人的運(yùn)行成績:每天5億次頁面瀏覽、每秒四萬次查詢請求峰值、每天新數(shù)據(jù)存儲量高達(dá)約3TB、總支持服務(wù)器達(dá)到1000余臺。
大多數(shù)成功的新興企業(yè)都面臨著相似的困擾,由于從初來乍到的新成員一躍成為萬眾矚目的焦點(diǎn)角色,一道巨大的危險(xiǎn)鴻溝硬生生將原本孱弱的技術(shù)支持團(tuán)隊(duì)推向了風(fēng)口浪尖。招聘員工、擴(kuò)大基礎(chǔ)設(shè)施、維護(hù)舊有基礎(chǔ)設(shè)施的同時(shí),仍然只有寥寥數(shù)位工程師負(fù)責(zé)著每個月巨大的數(shù)據(jù)吞吐量。這樣的工作強(qiáng)度意味著我們在處理過程中必須有所側(cè)重,而做出這樣的選擇無疑相當(dāng)艱難。Tumblr目前也陷入了這樣的困境,他們的做法是創(chuàng)建起一個由二十位精力充沛的工程師組成的技術(shù)小組,在應(yīng)對日常事務(wù)的同時(shí)制訂出多種極富創(chuàng)意的解決方案。
Tumblr創(chuàng)立之初就將自己定位為典型的大型LAMP(即由定制組件構(gòu)成的系統(tǒng))應(yīng)用程序。如今他們前進(jìn)的方向是通過Scala、HBase、Redis、Kafka、Finagle以及以架構(gòu)為基礎(chǔ)的新穎單元共同打造出多種分布式服務(wù)模塊,借以構(gòu)成完整的Dashboard導(dǎo)航內(nèi)容。目前他們已經(jīng)開始嘗試修復(fù)PHP應(yīng)用程序中的短期問題、將故障部分抽離出來并利用服務(wù)機(jī)制進(jìn)行更正。
Tumblr發(fā)展的主題是在龐大的平臺規(guī)模之上實(shí)施成功轉(zhuǎn)型。即將LAMP堆棧轉(zhuǎn)變?yōu)橐惶赘咄黄菩蕴匦缘男露褩?,同時(shí)將原本適用于新興企業(yè)的小型團(tuán)隊(duì)擴(kuò)展為裝備精良、擅長開發(fā)工作的成熟團(tuán)隊(duì),進(jìn)而為用戶帶來大量新功能及基礎(chǔ)設(shè)施。為了幫助我們更透徹地理解Tumblr實(shí)現(xiàn)這一方針的具體做法,Tumblr公司分布式系統(tǒng)工程師Blake Matheny分享了他的心得體會。以下是Blake對Tumblr做出的信息匯總:
Tumblr官方網(wǎng)址:http://www.tumblr.com/
統(tǒng)計(jì)
- 每天5億次頁面瀏覽量
- 每月150億次以上頁面瀏覽量
- 約20位技術(shù)工程師
- 每秒4萬次查詢請求峰值
- Hadoop集群每天接受超過1TB的數(shù)據(jù)量
- MySQL/HBase/Redis/Memcache等系統(tǒng)每天需要處理數(shù)以TB計(jì)的信息
- 每月用戶數(shù)量及處理負(fù)載增長30%
- 日常運(yùn)行中涉及約1000個硬件節(jié)點(diǎn)
- 每個月每位工程師平均要面對上億次頁面訪問活動
- 每天新增的博文及帖子約為50GB。新的用戶關(guān)注列表每天帶來約2.7TB的數(shù)據(jù)存儲量
- Dashboard系統(tǒng)每秒要應(yīng)對百萬次寫入操作、5萬次讀取操作,這一數(shù)字還在不斷增長之中
軟件
- 開發(fā)工作在OS X上進(jìn)行,日常運(yùn)行則交給Linux系統(tǒng)(CentOS、Scientific)
- Apache
- PHP, Scala, Ruby
- Redis, HBase, MySQL
- Varnish, HA-Proxy, nginx,
- Memcache, Gearman, Kafka, Kestrel, Finagle
- Thrift, HTTP
- Func - 一款安全且腳本化良好的遠(yuǎn)程控制框架及API
- Git, Capistrano, Puppet, Jenkins
硬件
- 500 臺web服務(wù)器
- 200臺數(shù)據(jù)庫服務(wù)器(其中大多數(shù)作為故障發(fā)生時(shí)的后備資源存在)
- 47個資源池
- 30個區(qū)塊
- 30臺緩存服務(wù)器
- 22 臺redis服務(wù)器
- 15 臺varnish服務(wù)器
- 25個haproxy節(jié)點(diǎn)
- 8 個nginx反向代理服務(wù)器
- 14臺作業(yè)隊(duì)列服務(wù)器(kestrel+gearman)
架構(gòu)
- 比起其它社交型網(wǎng)絡(luò),Tumblr擁有一套完全不同的使用模式。
- 每天有五千多萬篇博文發(fā)表,每一篇都有數(shù)百次的平均瀏覽量。并不是說少數(shù)熱門用戶具備的數(shù)百萬關(guān)注者拉升了平均瀏覽量,事實(shí)上每位Tumblr用戶基本都有成百上千的關(guān)注者。這與以往任何類型的社交網(wǎng)絡(luò)都有所不同,也恰恰是這種特性讓Tumblr面臨著史無前例的規(guī)?;简?yàn)。
- Tumblr目前是用戶傾注時(shí)間第二多的熱門社交網(wǎng)絡(luò),其內(nèi)容也極具吸引力。由于允許用戶自由分享圖片與視頻,因此博文的體積不再以字節(jié)計(jì)算。盡管用戶不一定總會發(fā)布體積龐大的內(nèi)容,但網(wǎng)站保證每個人都在需要時(shí)具備豐富自己文章的能力。人們樂于撰寫更為深刻的主題,這也讓關(guān)注者們找到了閱讀的樂趣,并持續(xù)花費(fèi)大量時(shí)間訪問Tumblr。
- 用戶之間構(gòu)成一套完整的聯(lián)絡(luò)紐帶,因此他們常常會嘗試回溯Dashboard中數(shù)百頁之前的內(nèi)容。其它社交網(wǎng)絡(luò)則往往只提供信息流,用戶體驗(yàn)只停留在驚鴻一瞥的層面上。
- 也就是說,由于用戶數(shù)量之大、每位用戶的平均閱讀數(shù)量之多以及用戶發(fā)起各類活動積極性之高,Tumblr需要處理的上傳信息龐大到令人驚愕。
- Tumblr運(yùn)行于一套托管站點(diǎn)中。設(shè)計(jì)者們?yōu)榫W(wǎng)站留出了充分的地理分布空間,以應(yīng)對未來的發(fā)展需求。
- Tumblr平臺由兩大組件構(gòu)成:Public Tumblelog與Dashboard系統(tǒng)。
- Public Tumblelog 是一款面向公眾的博客。由于動態(tài)特性較弱,因此緩存更易于打理。
- Dashboard在功能上與Twitter的timeline頗為相近。用戶將實(shí)時(shí)接收到自己關(guān)注對象的更新信息。
- 在規(guī)模特性上與其它博客載體頗為不同。緩存在這里的作用不再明顯,因?yàn)槊織l請求都各不相同,尤其是對于那些活躍的關(guān)注者而言。
- 運(yùn)行中必須保持高度的實(shí)時(shí)性與一致性,不應(yīng)顯示陳舊數(shù)據(jù),且需要處理的數(shù)據(jù)量非常龐大。每天的博文內(nèi)容本身只有50GB,但關(guān)注者列表更新信息則高達(dá)2.7TB。媒體文件全部存儲于內(nèi)存中。
- 大多數(shù)用戶將Tumblr作為內(nèi)容型消費(fèi)工具。每天頁面瀏覽量超過5億次,70%的瀏覽行為針對Dashboard系統(tǒng)。
- Dashboard系統(tǒng)的可用性相當(dāng)值得稱道。Tumblelog則一直有所欠缺,因?yàn)槠渲械哪程自谢A(chǔ)設(shè)施目前很難實(shí)施遷移。鑒于技術(shù)支持團(tuán)隊(duì)的規(guī)模太小,他們不得不在規(guī)?;M(jìn)程中優(yōu)先處理那些時(shí)間短、見效快的內(nèi)容。
過去的Tumblr
- Tumblr起初立足于Rackspace公司,后者為每個自定義域名博客創(chuàng)建一套A記錄。到了2007年,他們已經(jīng)擁有極為龐大的用戶群體,為了實(shí)施整體遷移,他們開始嘗試獨(dú)立于Rackspace公司之外。這時(shí)他們的自定義域業(yè)務(wù)仍然由Rackspace負(fù)責(zé),為了保持用戶的訪問方式,他們利用HAProxy與Varnish代理服務(wù)器將Rackspace域名轉(zhuǎn)向自己的服務(wù)器空間。類似的歷史遺留問題還有很多。
- 一套傳統(tǒng)的LAMP。
- 一直使用PHP,幾乎每位工程師都通過PHP處理編程工作。
- 創(chuàng)立之初只擁有一臺web服務(wù)器、一臺數(shù)據(jù)庫服務(wù)器以及一套PHP應(yīng)用程序。
- 為了完成規(guī)?;瘮U(kuò)展,他們開始使用memcache,接著引入前端緩存,然后是將HAProxy置于緩存之前,最后采用MySQL分區(qū)。MySQL分區(qū)體系的加入使整體服務(wù)效果邁上新的臺階。
- 力圖將所有處理負(fù)載控制在一臺服務(wù)器的承受能力之內(nèi)。在過去的一年中,他們借助C語言開發(fā)出兩款后端服務(wù):ID生成器與Staircar,Dashboard通知功能則利用Redis實(shí)現(xiàn)。
- Dashboard采用分散-集中式處理方案,當(dāng)用戶訪問Dashboard時(shí)事件將自動予以顯示。而讓用戶關(guān)注對象的新事件以推送形式顯示又花了技術(shù)團(tuán)隊(duì)六個月的時(shí)間。由于數(shù)據(jù)以時(shí)間為標(biāo)準(zhǔn)排序,因此分區(qū)設(shè)計(jì)方案的表現(xiàn)并不盡如人意。
如今的Tumblr
- 出于提高租用及開發(fā)速度的考量,如今采用JVM中央方案。
- 目標(biāo)是將所有存在于PHP應(yīng)用中的內(nèi)容遷移至服務(wù)項(xiàng)目中,并將PHP應(yīng)用本身作為新建的層置于服務(wù)之上,用于負(fù)責(zé)請求驗(yàn)證及介紹說明等工作。
- 選擇使用Scala與Finagle。
- 公司內(nèi)部擁有大量擅長Ruby及PHP開發(fā)工作的成員,因此Scala的推廣也就更為輕松。
- Finagle的使用也是他們選擇Scala的重要原因之一。這是一套來自Twitter的庫,能夠處理大多數(shù)由分布式設(shè)施帶來的技術(shù)難題,包括分布式跟蹤、服務(wù)項(xiàng)目搜索以及服務(wù)項(xiàng)目注冊等。當(dāng)然這些功能我們也不必全部采用,根據(jù)實(shí)際需求選擇即可,反正都是免費(fèi)的。
- 一旦與JVM環(huán)境相結(jié)合,F(xiàn)inagle能夠提供他們所需要的一切基本要素(例如Thrift、ZooKeeper等等)。
- Foursquare及Twitter一直在使用Finagle,Scala也始終效力于Meetup網(wǎng)站。
- 與Thrift應(yīng)用程序接口一樣,F(xiàn)inagle也帶來了相當(dāng)優(yōu)異的性能表現(xiàn)。
- 希望得到像Netty那樣的運(yùn)行效果,又不想涉及Java,因此Scala是最好的選擇。
- 采用Finagle是因?yàn)樗δ軓?qiáng)勁、所需知識并不冷門,使用中不涉及過多網(wǎng)絡(luò)代碼而且能為分布式系統(tǒng)提供一切所需功能。
- Node.js沒有入選的原因在于,它在JVM環(huán)境下對于技術(shù)團(tuán)隊(duì)的規(guī)模要求比較高。Node.js在開發(fā)方面缺乏標(biāo)準(zhǔn)化及最佳實(shí)踐選項(xiàng),測試用代碼也相對較少。而Scala允許我們使用任何現(xiàn)有Java代碼。而且在不要求過多知識儲備的基礎(chǔ)上,Scala能夠輕松應(yīng)對未來可能出現(xiàn)的擴(kuò)展性要求,同時(shí)保障5毫秒響應(yīng)時(shí)間、49秒雙機(jī)集群切換以及平均每秒四萬次、峰值四十萬次的請求處理。同時(shí)Java體系中的大量資源也可為我們所用。
- 內(nèi)部服務(wù)項(xiàng)目正由以C語言/libevent函式庫為基礎(chǔ)向Scala/Finagle為基礎(chǔ)轉(zhuǎn)變。
- 采用更新的HBase以及Redis等非關(guān)系類數(shù)據(jù)存儲機(jī)制,但目前大多數(shù)數(shù)據(jù)存儲在一套高度分區(qū)下的MySQL架構(gòu)中。尚沒有用HBase徹底替代MySQL。
- HBase憑借數(shù)以億計(jì)的URL以及全部歷史數(shù)據(jù)與分析結(jié)果支持自身的URL簡寫功能,運(yùn)行表現(xiàn)一直穩(wěn)固可靠。HBase主要用于處理高寫入請求情況,例如每秒上百萬次寫入動作的Dashboard重置操作。HBase沒有被用來替代MySQL的原因在于,Tumblr目前的技術(shù)支持團(tuán)隊(duì)尚無法在HBase上完成全部業(yè)務(wù)需求,因此他們最終決定先在規(guī)模較小的非關(guān)鍵性項(xiàng)目上進(jìn)行試用,盡量積累使用經(jīng)驗(yàn)。
- 以MySQL及分區(qū)機(jī)制為基礎(chǔ),將按時(shí)間排序的所有數(shù)據(jù)納入同一個分區(qū)的過程始終麻煩不斷。讀取及復(fù)制操作也由于子集群寫入動作的同時(shí)發(fā)生而常常出現(xiàn)滯后現(xiàn)象。
- 創(chuàng)建出一套通用型服務(wù)框架。
- 事先花費(fèi)大量時(shí)間制訂方案,用以解決分布式系統(tǒng)在管理時(shí)出現(xiàn)的操作問題。
- 創(chuàng)建一套用于服務(wù)項(xiàng)目的Rails橋架,作為引導(dǎo)內(nèi)部服務(wù)的模板使用。
- 所有服務(wù)項(xiàng)目從運(yùn)行的角度來看都完全一致。各服務(wù)的狀態(tài)檢查、監(jiān)控、啟用以及中止都以同樣的方式進(jìn)行。
- 創(chuàng)建過程中使用的工具為SBT(一款Scala創(chuàng)建工具),同時(shí)用到的還有其它一些插件及輔助內(nèi)容,旨在為包括git項(xiàng)目標(biāo)注、倉庫信息發(fā)布等公共活動提供支持。大多數(shù)開發(fā)人員不必深入了解系統(tǒng)的創(chuàng)建過程。
- 前端層使用HAProxy,Varnish則服務(wù)于公共博客,二者共計(jì)占用40臺設(shè)備。
- Apache及各類PHP應(yīng)用程序由500臺網(wǎng)絡(luò)服務(wù)器予以支持。
- 數(shù)據(jù)庫服務(wù)器共有200臺。大部分?jǐn)?shù)據(jù)庫服務(wù)器用于提高整體可用性。由于使用標(biāo)準(zhǔn)化硬件配置,因此平均無故障工作時(shí)間相當(dāng)令人滿意。硬件設(shè)備的損耗大大超出預(yù)期,因此技術(shù)人員準(zhǔn)備了大量后備物資以應(yīng)對不時(shí)之需。
- 六項(xiàng)用于支持PHP應(yīng)用程序的后端服務(wù)。一個專項(xiàng)小組專門負(fù)責(zé)開發(fā)此類后端服務(wù)。每隔兩到三周都會有一款新服務(wù)推出,例如Dashboard通知、Dashboard輔助索引、URL簡寫工具以及用于分區(qū)處理的緩存代理器等。
- 在MySQL分區(qū)方面投入大量時(shí)間以及人力物力。盡管MongoDB目前在紐約(Tumblr的總部所在地)風(fēng)靡一時(shí),但他們?nèi)匀徊捎昧藬U(kuò)展性更好的MySQL。
- Gearman,一款作業(yè)隊(duì)列系統(tǒng),被用于處理運(yùn)行時(shí)間較長且無人照看的工作內(nèi)容。
- 可用性評估以實(shí)際使用效果為標(biāo)準(zhǔn)。用戶能夠正常訪問自定義域名或是Dashboard嗎?另外故障率也是評估中的一項(xiàng)因素。
- 就長期運(yùn)行狀況來看,具備最高優(yōu)先級的項(xiàng)目必須首先得到修復(fù)。目前故障模式已經(jīng)得到有效的分析與系統(tǒng)性解決,其目的是同時(shí)從用戶及應(yīng)用程序的角度來評估整體運(yùn)行狀態(tài)。如果一條請求中的某部分沒有得到充分響應(yīng),那么這套評估機(jī)制必須迅速查明原因。
- 最初的角色模式是由Finagle負(fù)責(zé)支持的,但后來這種方式被棄之不用,取而代之的是一套無需照看的作業(yè)隊(duì)列。另外,Twitter的實(shí)用庫中包含一套名為Futures的服務(wù)實(shí)施方案。當(dāng)某個線程池需要被調(diào)用時(shí),F(xiàn)utures服務(wù)將立即建立一個future池,這時(shí)所有待處理內(nèi)容都將被提交至future池中以進(jìn)行異步執(zhí)行。
- 由于自身特性的原因,Scala并不適合處理共享狀態(tài)。Finagle則可以默認(rèn)為適合處理此類情況,因?yàn)門witter已經(jīng)將付諸實(shí)際應(yīng)用。在Scala及Finagle中都應(yīng)盡量避免可變狀態(tài)調(diào)用架構(gòu)的情況。不能讓設(shè)備長期處于運(yùn)行狀態(tài)。狀態(tài)信息采集自數(shù)據(jù)庫,并在使用后重新寫入數(shù)據(jù)庫。這么做的優(yōu)勢是,開發(fā)人員們不必?fù)?dān)心線程或者鎖定問題。
- 22臺Redis服務(wù)器。每臺服務(wù)器運(yùn)行8到32個實(shí)例,也就是說共計(jì)有數(shù)百個Redis實(shí)例用于生產(chǎn)流程。
- 后端存儲機(jī)制用于為Dashboard通知功能提供支持。
- 通知功能本身類似于關(guān)注我們博文的某位用戶。通知會顯示在用戶的Dashboard中,這樣我們就能及時(shí)了解其它用戶的最新動態(tài)。
- 極高的寫入頻率使得MySQL有些力不從心。
- 通知內(nèi)容無需長期存在,因此即使用戶掉線也不會出現(xiàn)突然涌入大量提示消息的窘境,這樣一來Redis就成為實(shí)現(xiàn)通知功能的選擇之一。
- 盡量為技術(shù)人員創(chuàng)造機(jī)會,讓他們學(xué)習(xí)Redis的相關(guān)知識并熟悉其工作原理。
- 由于Redis擁有強(qiáng)大的社區(qū)體系,因此無論遇上什么問題都能在這里找到解決方法。
- 為Redis創(chuàng)建一個以Scala futures為基礎(chǔ)的接口,這項(xiàng)功能現(xiàn)在正逐漸轉(zhuǎn)到Cell架構(gòu)當(dāng)中。
- URL簡寫工具將Redis作為一級緩存,并把HBase當(dāng)成長效存儲機(jī)制。
- Dashboard的輔助索引圍繞Redis進(jìn)行創(chuàng)建。
- Redis作為Gearman的持久化層存在,并用到了由Finagle創(chuàng)建的緩存代理器。
- 慢慢由緩存向Redis遷移。希望能夠以一套快取服務(wù)作為最終方案,其性能表現(xiàn)應(yīng)與緩存方案一致。
內(nèi)部傳輸線
內(nèi)部應(yīng)用程序需要訪問活動流。每個活動流的內(nèi)容都與用戶行為相關(guān),例如創(chuàng)建、刪除博文或者喜歡、反感某些博文等。挑戰(zhàn)在于如何將這樣規(guī)模的數(shù)據(jù)實(shí)時(shí)加以分散。要達(dá)到這一目的,需要足以支持大規(guī)模內(nèi)部擴(kuò)展的工具以及擁有可靠生態(tài)系統(tǒng)輔助的應(yīng)用程序。此外還要確立分布式系統(tǒng)的中心點(diǎn)。
- 之前信息的分布化由Scribe/Hadoop負(fù)責(zé)。服務(wù)項(xiàng)目要首先登入Scribe并開始追蹤,然后將數(shù)據(jù)傳輸至應(yīng)用程序端。這種模式讓可擴(kuò)展性成為空談,尤其是在用戶每秒創(chuàng)建上千篇博文的峰值時(shí)段。應(yīng)該盡量避免用戶追蹤文件并進(jìn)行打印。
- 創(chuàng)建一套類似于信息公交車這樣的內(nèi)部傳輸線,服務(wù)項(xiàng)目與應(yīng)用程序通過Thrift與傳輸線進(jìn)行交互。
- 利用來自LinkedIn網(wǎng)站的Kafka來存儲消息。內(nèi)部用戶使用HTTP流直接從傳輸線上閱讀內(nèi)容。不使用MySQL的原因在于,分區(qū)工作本身就過于頻繁,因此不適合讓其承擔(dān)大量數(shù)據(jù)流。
- Tumblr的傳輸線模式靈活性極強(qiáng),Twitter所使用的傳輸線則相形見絀,其中的數(shù)據(jù)常常面臨丟失。
- 傳輸線流能夠及時(shí)進(jìn)行回溯,它會保存一周以內(nèi)的所有數(shù)據(jù)。在連接時(shí),它可以正確找回最后一次閱讀的位置。
- 允許同時(shí)連入多個客戶端,而且每個客戶端所看到的內(nèi)容都各不相同。每個客戶端擁有獨(dú)立的客戶端ID,這要?dú)w功于Kafka所支持的用戶組概念。用戶組中的每一個用戶不僅會看到與自己相關(guān)的消息,并且消息內(nèi)容絕不重復(fù)。可以利用同一個用戶ID創(chuàng)建多個客戶端,這些客戶端所看到的內(nèi)容同樣不會重復(fù)。也就是說數(shù)據(jù)以獨(dú)立且并行的方式進(jìn)行處理。Kafka通過ZooKeeper定期為用戶設(shè)置檢查點(diǎn),以記錄用戶的當(dāng)前閱讀進(jìn)度。
Dashboard收件箱的Cell設(shè)計(jì)
- 目前為Dashboard功能提供支持的分散-集中模式存在著一定局限性,不久之后就會轉(zhuǎn)而采用更先進(jìn)的方案。
- 解決方案是讓收件箱采用以Cell為基礎(chǔ)的新型架構(gòu),這與Facebook的Messages功能頗為相似。
- 收件箱可以說是分散-集中模式的對立面。由關(guān)注者的博文及近期動態(tài)所構(gòu)成的Dashboard版面,按時(shí)間順序及邏輯方式存儲在一起。
- 由于收件箱的功能特性,分散-集中模式帶來的問題在這里不攻自破。我們只會詢問收件箱中保存著哪些內(nèi)容,這完全不涉及關(guān)注者、用戶好友之類亂七八糟的關(guān)系。這樣的情況將持續(xù)很長一段時(shí)間。
- 對Dashboard內(nèi)容進(jìn)行重寫是相當(dāng)困難的。雖然數(shù)據(jù)擁有分布式特性,但它同時(shí)也擁有交互式特性,因此用戶無法實(shí)現(xiàn)局部更新功能。
- 數(shù)據(jù)總量之大簡直超乎想象。每一條消息平均要發(fā)送給成百上千位不同用戶,這比Facebook的工作負(fù)擔(dān)更重。大量數(shù)據(jù)加上高分布率再加上多個數(shù)據(jù)中心協(xié)同運(yùn)作,整個流程的復(fù)雜程度由此可見一斑。
- 每秒百萬次寫入操作及五萬次讀取操作,這么高強(qiáng)度的交互活動帶來了高達(dá)2.7TB的數(shù)據(jù)集增長量,還不算復(fù)制或是壓縮等處理工作。由24個字節(jié)構(gòu)成的百萬次寫入操作給收件箱帶來海量信息。
- 以上所有工作由一套應(yīng)用程序負(fù)責(zé),因此保證程序的正常運(yùn)作就顯得至關(guān)重要。
- Cell單元。
- 每個Cell單元都是一套獨(dú)立的裝置,其中包含了大量用戶的全部相關(guān)數(shù)據(jù)。用戶Dashboard版面所需要的一切數(shù)據(jù)都由Cell單元提供。
- 用戶以映射的方式與Cell單元連通,每個數(shù)據(jù)中心都運(yùn)行著大量Cell單元。
- 每個Cell單元都擁有自己的一套HBase集群、服務(wù)集群以及Redis緩存集群。
- 用戶與Cell單元相對應(yīng),所有Cell單元都通過傳輸線中的上傳數(shù)據(jù)調(diào)用博客信息。
- 每個Cell單元都以Finagle為基礎(chǔ),并通過傳輸線以及Thrift上的服務(wù)請求構(gòu)成HBase數(shù)據(jù)信息。
- 當(dāng)某位用戶進(jìn)入Dashboard版面,就會從特定Cell單元中調(diào)出多個相關(guān)用戶的信息;服務(wù)器節(jié)點(diǎn)通過HBase讀取相關(guān)用戶的Dashboard內(nèi)容并將數(shù)據(jù)傳回當(dāng)前登錄用戶。
- 后臺任務(wù)從傳輸線中獲取信息,并生成對應(yīng)列表及處理請求。
- Cell單元中的博客信息會調(diào)用Redis緩存層。
- 請求指令流:當(dāng)某位用戶發(fā)布博文,該篇博文即會被寫入傳輸線;所有Cell單元從中讀取博文內(nèi)容并將其轉(zhuǎn)寫入文章數(shù)據(jù)庫中;接著Cell單元會檢查該文用戶的關(guān)注者中有哪些處于自身單元內(nèi)部,最終將尋獲的關(guān)注者收件箱以及博文ID共同進(jìn)行上傳。
- Cell單元設(shè)計(jì)的優(yōu)勢所在:
- 請求指令的大量同時(shí)出現(xiàn)勢必對并行處理能力要求極高,這就使得設(shè)備組件必須彼此獨(dú)立,然而獨(dú)立性也會導(dǎo)致交互過程無法完成。Cell單元設(shè)計(jì)使并行處理工作成為一個獨(dú)立體系內(nèi)部的事務(wù),并能夠在用戶群體不斷增長的情況下隨時(shí)保持強(qiáng)大的可調(diào)節(jié)能力。
- Cell單元天然具備故障隔離能力。某個Cell單元出錯不會對其它單元造成任何嚴(yán)重影響。
- Cell單元使更新內(nèi)容測試、信息發(fā)布以及軟件版本測試工作得以有條不紊地進(jìn)行。
- 容易被大家忽略的關(guān)鍵性環(huán)節(jié)是:所有文章都會被復(fù)制到每一個Cell單元中去。
- 每個Cell單元都存儲著一套獨(dú)立的全局文章副本。每個Cell單元都能夠?yàn)镈ashboard版面提供令人滿意的內(nèi)容。應(yīng)用程序并不會查詢所有文章ID,它只會為登錄ID查詢相關(guān)文章,這樣一來用戶將能夠在自己的Dashboard版面中看到所有信息。每一個Cell單元都擁有填充Dashboard所必需的全部數(shù)據(jù),因此不必進(jìn)行什么跨單元通訊。
- 共使用了兩套HBase列表:一套用于存儲文章副本,而另一套則為Cell單元中的每個用戶保存博文ID,列表之間的內(nèi)容基本無甚關(guān)聯(lián)。第二套列表的作用是為用戶的Dashboard版面提供顯示內(nèi)容,也就是說不必追蹤該用戶所關(guān)注的每位其它用戶。另外,利用這種機(jī)制,如果我們在另一臺設(shè)備上閱讀或?yàn)g覽某篇文章,系統(tǒng)將不會認(rèn)為我們是在重復(fù)讀取同樣的內(nèi)容。無論通過何種方式,我們的閱讀進(jìn)度都將保存在收件箱模塊的狀態(tài)一項(xiàng)中。
- 文章不會被直接置于收件箱中,因?yàn)檫@么多文章累積起來容量實(shí)在驚人。因此只將ID放入收件箱,而文章內(nèi)容則由Cell單元保存。這種方式大幅度降低了所需存儲空間,并讓用戶收件箱中的內(nèi)容按時(shí)間順序變得非常簡便。這么做的缺點(diǎn)是每個Cell單元都需要包含完整的文章副本。但令人驚訝的是,文章本身的體積總量遠(yuǎn)遠(yuǎn)小于映射所需的存儲空間。每天每個Cell單元中的文章內(nèi)容增量約為50GB,但收件箱內(nèi)容的增量卻高達(dá)每天2.7TB。由此可見,用戶的閱讀量比寫入量大得多。
- 用戶的Dashboard中并不顯示文章內(nèi)容,只包含基本的文章ID;因此數(shù)據(jù)增量的主體是不斷膨脹的ID。
- 即使用戶的關(guān)注者名單發(fā)生變更,這種設(shè)計(jì)仍然完全應(yīng)付得來,因?yàn)樗形恼露家呀?jīng)存在于Cell單元中。如果Cell單元中只保存來自關(guān)注者的文章,那么一旦關(guān)注者名單有所變動,單元中的數(shù)據(jù)將不足以及時(shí)做出反應(yīng),同時(shí)將引發(fā)一系列不可避免的信息填充活動。
- 另有一套備選方案,利用一套獨(dú)立的文章集群存儲博文內(nèi)容。這種設(shè)計(jì)的缺點(diǎn)在于,一旦這套集群出現(xiàn)故障,那么整個網(wǎng)站都將陷入癱瘓。相比之下,采用Cell單元設(shè)計(jì)、將所有文章復(fù)制到每一個單元當(dāng)中的做法使整個架構(gòu)牢固而可靠。
- 對于那些關(guān)注人數(shù)以百萬計(jì)且相當(dāng)活躍的用戶,會根據(jù)他們的訪問模式獲得特定的用戶服務(wù)。
- 不同的用戶往往使用不同的訪問模式以及與個人習(xí)慣相符的分布模式。Tumblr為用戶準(zhǔn)備了兩種不同的分布模式:一種適合人氣超高的熱門用戶、另一種則適合任何類型的用戶。
- 數(shù)據(jù)的處理方式也根據(jù)用戶類型的變化而有所不同。來自活躍用戶的文章實(shí)際上不會真正予以發(fā)布,而只是有選擇地呈現(xiàn)給其他用戶。
- Tumblr在處理大量關(guān)注其他使用者的用戶時(shí),采用的方法與處理擁有大量關(guān)注者的用戶非常類似。
- 每個Cell單元的大小很難確定。單元的大小會影響到網(wǎng)站的故障機(jī)率,而單元中所包含用戶的數(shù)量則是最關(guān)鍵的因素。需要在用戶的使用期望與支撐這種期望的成本支出之間找到一個平衡點(diǎn)。
- 從傳輸線中讀取信息是網(wǎng)絡(luò)問題的核心內(nèi)容。在Cell單元內(nèi)部,網(wǎng)絡(luò)流量始終處于可管理的狀態(tài)。
- 由于Cell單元的數(shù)量日益增加,最終Cell單元組的概念被添加進(jìn)來。這是一套分層復(fù)制規(guī)劃,同時(shí)也有利于將數(shù)據(jù)信息遷移到多個數(shù)據(jù)中心當(dāng)中。
成為一顆立足于紐約的耀眼新星
- 紐約的環(huán)境背景相當(dāng)特殊,其中充斥著大量財(cái)務(wù)與廣告因素。作為一家缺乏經(jīng)驗(yàn)的新興企業(yè),Tumblr在人才招聘及設(shè)備租用方面都面臨著挑戰(zhàn)。
- 在過去幾年中,紐約市一直致力于扶植新興企業(yè)。紐約大學(xué)與哥倫比亞大學(xué)制訂了完善的計(jì)劃,鼓勵在校學(xué)生到新興企業(yè)中去實(shí)習(xí),而不是一頭扎進(jìn)華爾街。Bloomberg市長也將支持科技發(fā)展作為紐約市的一項(xiàng)基本戰(zhàn)略。
團(tuán)隊(duì)構(gòu)成
- 團(tuán)隊(duì):基礎(chǔ)設(shè)施、平臺、SRE(即系統(tǒng)調(diào)整及修復(fù))、產(chǎn)品、網(wǎng)絡(luò)運(yùn)行以及服務(wù)支持。
- 基礎(chǔ)設(shè)施:5層及以下。IP地址及以下、DNS、硬件供應(yīng)。
- 平臺:核心應(yīng)用程序開發(fā)、SQL分區(qū)、服務(wù)、網(wǎng)絡(luò)運(yùn)行。
- SRE: 存在于服務(wù)團(tuán)隊(duì)與網(wǎng)絡(luò)運(yùn)行團(tuán)隊(duì)之間,致力于解決迫在眉睫的系統(tǒng)可靠性及可擴(kuò)展性問題。
- 服務(wù)團(tuán)隊(duì):致力于發(fā)展戰(zhàn)略層面的問題,處理周期一般為一到兩個月。
- 網(wǎng)絡(luò)運(yùn)行:負(fù)責(zé)問題檢測與響應(yīng)以及相關(guān)調(diào)整工作。
軟件開發(fā)
- 一切工作以一套專門將PHP應(yīng)用程序分布到各處的rsync腳本集合為基礎(chǔ)。一旦設(shè)備總數(shù)達(dá)到200臺,系統(tǒng)就會發(fā)生故障——部署流程無比緩慢、設(shè)備自身也停滯在各種各樣的配置狀態(tài)中。
- 在此之后,Tumblr決定將部署流程(包括開發(fā)、啟用及生產(chǎn)等環(huán)節(jié))利用Capistrano創(chuàng)建在服務(wù)堆棧中。這在數(shù)十臺設(shè)備協(xié)作的情況下沒有問題,但通過SSH讓數(shù)百臺設(shè)備彼此連接后,問題再一次出現(xiàn)。
- 現(xiàn)在所有設(shè)備上都運(yùn)行著一套調(diào)整軟件,這套軟件以紅帽Func為基礎(chǔ),本質(zhì)上是一個用于將指令分配到各主機(jī)處的輕量型API。規(guī)模化要素也被部署在Func內(nèi)部。
- 開發(fā)工作在Func層面上進(jìn)行,并采用“在某個主機(jī)組上執(zhí)行某操作”的直觀表述,這就成功回避了對SSH的依賴。例如我們希望在A組中部署軟件,那么管理員只需選定目標(biāo)節(jié)點(diǎn)集合并運(yùn)行部署指令,即可完成任務(wù)。
- 部署指令通過Capistrano進(jìn)行實(shí)施,并能夠以git檢查或者倉庫輸出的形式生效。擴(kuò)展性方面也有所保障,因?yàn)橹噶蠲嫦虻慕换ο鬄镠TTP。之所以采用Capistrano,是因?yàn)樗茉诹己玫陌姹究刂浦轮С趾唵文夸洠@種特性使其與PHP應(yīng)用程序之間的配合更為默契。以版本控制更新活動使得每個目錄都包含安全散列算法,這樣技術(shù)人員也能夠方便地核對當(dāng)前版本是否正確。
- Func API的作用是回饋狀態(tài),通知系統(tǒng)哪些設(shè)備正在運(yùn)行哪些版本的軟件。
- 任何服務(wù)項(xiàng)目都能夠安全地加以重啟,因?yàn)槟J(rèn)狀態(tài)下項(xiàng)目將首先逐漸斷開連接、之后才實(shí)施重啟動作。
- 每項(xiàng)功能在投付使用之前,都會在獨(dú)立環(huán)境下進(jìn)行測試。
開發(fā)工作
- Tumblr所秉承的理論是讓每位用戶都能使用自己想要的工具,但隨著用戶群體的激增,這種想法實(shí)在難以為繼。招聘精明強(qiáng)干的技術(shù)人員不是件容易的事,因此他們開始對堆棧進(jìn)行標(biāo)準(zhǔn)化改造,旨在讓技術(shù)工作更好上手、團(tuán)隊(duì)培養(yǎng)更加高效、產(chǎn)品問題的處理更加迅速并在上述內(nèi)容的基礎(chǔ)上建立業(yè)務(wù)運(yùn)作。
- 整個開發(fā)過程與Scrum比較相似。選擇的是工作量較小的輕量級模式。
- 每位開發(fā)人員都擁有一臺經(jīng)過預(yù)先配置的開發(fā)專用設(shè)備。設(shè)備更新工作則通過Puppet實(shí)現(xiàn)。
- 開發(fā)用設(shè)備可以隨時(shí)回收并進(jìn)行變更及測試,接著重新投入使用并最終執(zhí)行生產(chǎn)任務(wù)。
- 開發(fā)人員們常用的文本編輯器是Vim和Textmate。
- PHP應(yīng)用程序的測試以代碼審核的方式進(jìn)行。
- 他們在服務(wù)項(xiàng)目端部署了一套測試用基礎(chǔ)設(shè)施,其中包括hooks、Jenkins以及連續(xù)性集成與通行創(chuàng)建系統(tǒng)。
招聘流程
- 面試階段一般不會涉及數(shù)學(xué)、分析以及腦力測驗(yàn)。提出的問題主要圍繞著求職者所申請的職位展開。他們是否聰慧?能否順利完成份內(nèi)的工作?但評估求職者能否“順利完成份內(nèi)工作”有一定難度??傊嬖嚨哪康氖钦页錾碡?fù)才能的新人,而不是有意排除一定比例的求職者。
- 編碼能力是考核的一大重點(diǎn)。他們會針對示例代碼提出問題,在電話面試的過程中也常常利用Collabedit考驗(yàn)求職者的共享代碼編寫能力。
- 面試本身并不具有對抗性,他們只是在嘗試發(fā)現(xiàn)優(yōu)秀的人才。求職者們可以在面試中使用像谷歌這樣的輔助工具來解決問題,因?yàn)門umblr相信善于使用外界助力的開發(fā)人員才是能夠獨(dú)當(dāng)一面的上上之選,所以在考核中并不禁止使用輔助手段。
- 鑒于Tumblr所要應(yīng)對的數(shù)據(jù)流量相當(dāng)巨大,因此招徠在規(guī)?;瘶I(yè)務(wù)方面具備豐富經(jīng)驗(yàn)的人才正是當(dāng)務(wù)之急。事實(shí)上在全球范圍內(nèi),還沒有幾家企業(yè)需要處理如此規(guī)模的日常工作。
- 舉例來說,他們需要一款新的ID生成器,要求服務(wù)運(yùn)行環(huán)境為JVM,在每秒一萬條請求的使用頻率下保證響應(yīng)時(shí)間低于1毫秒,并且要在內(nèi)存限定為500MB的前提下保證高可用性。結(jié)果在招聘過程中,他們發(fā)現(xiàn)了幾位牛人,不僅嚴(yán)格按照給定條件執(zhí)行、還將響應(yīng)延遲降低到新的水平。這也最終使得Tumblr愿意花費(fèi)大量時(shí)間對JVM運(yùn)行環(huán)境進(jìn)行調(diào)整。
- 在Tumblr的工程博客中,他們專門發(fā)表文章對Dennis Ritchie與John McCarthy表示敬意及紀(jì)念,這正是公司極客氛圍的實(shí)際體現(xiàn)。
經(jīng)驗(yàn)教訓(xùn)
- 自動化流程無處不在。
- MySQL(與分區(qū)機(jī)制)具備可擴(kuò)展性,而應(yīng)用程序本身則不行。
- Redis功能驚人。
- Scala應(yīng)用程序的性能表現(xiàn)令人贊嘆。
- 在不確定某個項(xiàng)目能否起效時(shí),果斷加以廢除。
- 不要過分在意那些在垃圾技術(shù)領(lǐng)域殘喘多年積累到的所謂經(jīng)驗(yàn)。真正重要的是他們是否適合自己的團(tuán)隊(duì)、是否能勝任新的工作崗位。
- 選擇一套能幫你雇用到所需人才的招聘機(jī)制。
- 根據(jù)技術(shù)團(tuán)隊(duì)的實(shí)際狀況定制招聘標(biāo)準(zhǔn)。
- 盡量多讀些技術(shù)論文以及博客文章。像Cell單元架構(gòu)以及選擇性呈現(xiàn)這樣的好點(diǎn)子往往并非原創(chuàng)。
- 多與同事們交流。他們中肯定有人會與來自Facebook、Twitter、LinkedIn等網(wǎng)站的工程師們探討工作經(jīng)歷并學(xué)習(xí)新知識。我們自己可能還沒有達(dá)到同樣的高度,但處處留心絕對有利無弊。
- 另外,別讓新人直接負(fù)責(zé)技術(shù)工作。在接觸實(shí)際生產(chǎn)流程之前,他們需要在試驗(yàn)性項(xiàng)目組或者工作壓力較小的職位上好好拿出時(shí)間學(xué)習(xí)HBase以及Redis。
在這里我要向Blake表示感謝,也正是由于他的耐心配合,這篇匯總型文章才會與大家見面。采訪過程中他表現(xiàn)出令人稱道的慷慨心態(tài),并且不厭其煩地向我們解釋工作流程中的具體細(xì)節(jié)。
原文:Tumblr Architecture - 15 Billion Page Views a Month and Harder to Scale than Twitter