Tumblr 架構設計

發表於2013-05-26

| 2013-05-26 21:58      

  最近的新聞中我們得知雅虎11億美元收購了Tumblr: Yahoo bought Tumblr for $1.1 billion. 你也許會發現Instagram也被Facebook重金收購的介紹. 這是一個巧合嗎?這就由你來判斷吧

  為什麼雅虎會收購Tumblr? 這場交易中的商業價值我可能無法判斷,但是如果你對Tumblr的技術方面有所瞭解,你一定會為Tumblr豎起大拇指. 為什麼這麼說呢,請接著讀...

  Tumblr每月頁面瀏覽量超過150億次,已經成為火爆的部落格社群。使用者也許喜歡它的簡約、漂亮,並且它對使用者體驗強烈的關注,或是友好而忙碌的溝通方式,總之,它深得人們的喜愛。

  每月超過30%的增長當然不可能沒有挑戰,其中可靠性問題尤為艱鉅。每天5億次瀏覽量,峰值每秒4萬次請求,每天3TB新的資料儲存,並執行於超過1000臺伺服器上,所有這些幫助Tumblr實現巨大的經營規模。

  創業公司邁向成功,都要邁過危險的迅速發展期這道門檻。尋找人才,不斷改造基礎架構,維護舊的架構,同時要面對逐月大增的流量,而且曾經只有4位工程師。 這意味著必須艱難地選擇應該做什麼,不該做什麼。這就是Tumblr的狀況。好在現在已經有20位工程師了,可以有精力解決問題,並開發一些有意思的解決 方案。

  Tumblr最開始是非常典型的LAMP應用。目前正在向分散式服務模型演進,該模型基於ScalaHBaseRedisKafkaFinagle,此外還有一個有趣的基於Cell的架構,用於支援Dashboard .現在的重點被放在瞭解決他們PHP程式中的短期問題,找出問題,並正確的使用服務化去解決他們.

  Tumblr目前的最大問題是如何改造為一個大規模網站。系統架構正在從LAMP演進為最先進的技術組合,同時團隊也要從小的創業型發展為全副武裝、隨時待命的正規開發團隊,不斷創造出新的功能和基礎設施。下面就是Blake對Tumblr系統架構情況的介紹。

  Tumblr網址:  http://www.tumblr.com/

統計

  • 每天 5 億頁面瀏覽量
  • 每月超過 150億 頁面瀏覽量
  • 約 20 名工程師
  • 峰值每秒大約 4 萬請求
  • 每天有超過 1TB 的資料進入 Hadoop 叢集
  • 每天有幾個 TB 的資料進入 MySQL/HBase/Redis/Memcache
  • 每月增長 30%
  • 產品環境有大約 1000 個硬體節點
  • 平均每個工程師每月處理10億頁面訪問
  • 每天大約 50GB 的文章,關注者列表更新大約每天 2.7TB
  • Dashboard 每秒百萬次寫,每秒 5w 讀

軟體

  • 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 臺 memcache 伺服器
  • 22 臺 redis 伺服器
  • 15 臺 varnish 伺服器
  • 25 個 haproxy 節點
  • 8 個 nginx
  • 14 個作業佇列伺服器 (kestrel + gearman)

構架

  • 與其他社交網站不同的是,Tumblr有其獨特的使用模式。
    • 每天有超過5千萬篇文章更新,平均每篇文章的跟帖又數以百計。使用者一般只有數百個粉絲。這與其他社會化網站裡少數使用者有幾百萬粉絲非常不同,使得Tumblr的擴充套件性極具挑戰性。
    • 按使用者使用時間衡量,Tumblr已經是排名第二的社會化網站。內容的吸引力很強,有很多圖片和影片,文章往往不短,一般也不會太長,但允許寫得很長。文章內容往往比較深入,使用者會花費更長的時間來閱讀。
    • 使用者與其他使用者建立聯絡後,可能會在Dashboard上往回翻幾百頁逐篇閱讀,這與其他網站基本上只是部分資訊流不同。
    • 使用者的數量龐大,使用者的平均到達範圍更廣,使用者較頻繁的發帖,這些都意味著有巨量的更新需要處理。
  • Tumblr目前執行在一個託管資料中心中,已在考慮地域上的分散式構架。
  • Tumblr平臺由兩個元件構成:公共Tumblelogs和Dashboard
    • 公共Tumblelogs與部落格類似,並非動態,易於快取
    • Dashboard是類似於Twitter的時間軸,使用者由此可以看到自己關注的所有使用者的實時更新。
      • 與部落格的擴充套件性不同,快取作用不大,因為每次請求都不同,尤其是活躍的關注者。
      • 而且需要實時而且一致,文章每天僅更新50GB,跟帖每天更新2.7TB,所有的多媒體資料都儲存在S3上面。
    • 大多數使用者以Tumblr作為內容瀏覽工具,每天瀏覽超過5億個頁面,70%的瀏覽來自Dashboard。
    • Dashboard的可用性已經不錯,但Tumblelog一直不夠好,因為基礎設施是老的,而且很難遷移。由於人手不足,一時半會兒還顧不上。

老的Tumblr構架

  • Tumblr最開始是託管在Rackspace上的,每個自定義域名的部落格都有一個A記錄。當2007年Rackspace無法滿足其發展速度不得不遷移 時,大量的使用者都需要同時遷移。所以他們不得不將自定義域名保留在Rackspace,然後再使用HAProxy和Varnish路由到新的資料中心。類 似這樣的遺留問題很多。
  • 開始的架構演進是典型的LAMP路線
    • 最初用PHP開發,幾乎所有程式設計師都用PHP
    • 最初是三臺伺服器:一臺Web,一臺資料庫,一臺PHP
    • 為了擴充套件,開始使用memcache,然後引入前端cache,然後在cache前再加HAProxy,然後是非常奏效的MySQL sharding
    • 採用“在單臺伺服器上榨出一切”的方式。過去一年已經用C開發了兩個後端服務:ID generatorStaircar(用Redis進行Dashboard通知)
  • Dashboard採用了“擴散-收集”方式。當使用者訪問Dashboard時將顯示事件,來自所關注的使用者的事件是透過拉然後顯示的。這樣支撐了6個月。由於資料是按時間排序的,因此sharding模式不太管用。

新的構架

  • 由於招人和開發速度等原因,改為JVM為中心
  • 目標是將一切從PHP應用改為服務,使應用變成請求鑑別、呈現等諸多服務之上的薄層。
  • 選擇了Scala 和 Finagle
    • 在團隊內部有很多人具備Ruby和PHP經驗,所以Scala很有吸引力。
    • Finagle是選擇Scala的重要因素之一。這個來自Twitter的庫可以解決大多數分散式問題,比如分散式跟蹤、服務發現、服務註冊等。
    • 轉到JVM上之後,Finagle提供了團隊所需的所有基本功能(Thrift, ZooKeeper等),無需再開發許多網路程式碼,另外,團隊成員認識該專案的一些開發者。
    • Foursquare和Twitter都在用Finagle,Meetup也在用Scala。
    • 應用介面與Thrift類似,效能極佳。
    • 團隊本來很喜歡Netty,但不想用Java,Scala是不錯的選擇。
    • 選擇Finagle是因為它很酷,還認識幾個開發者。
    • 之所以沒有選擇Node.js,是因為以JVM為基礎更容易擴充套件。Node的發展為時尚短,缺乏標準、最佳實踐以及大量久經測試的程式碼。而用Scala的 話,可以使用所有Java程式碼。雖然其中並沒有多少可擴充套件的東西,也無法解決5毫秒響應時間、49秒HA、4萬每秒請求甚至有時每秒40萬次請求的問題。 但是,Java的生態鏈要大得多,有很多資源可以利用。
  • 內部服務從C/libevent為基礎正在轉向Scala/Finagle為基礎。
  • 開始採用新的NoSQL儲存方案如HBase和Redis。但大量資料仍然儲存在大量分割槽的MySQL架構中,並沒有用HBase代替MySQL。
  • HBase主要支援短地址生產程式(數以十億計)還有歷史資料和分析,非常結實。此外,HBase也用於高寫入需求場景,比如Dashboard重新整理時一 秒上百萬的寫入。之所以還沒有替換HBase,是因為不能冒業務上風險,目前還是依靠人來負責更保險,先在一些小的、不那麼關鍵的專案中應用,以獲得經 驗。
  • MySQL和時間序列資料sharding(分片)的問題在於,總有一個分片太熱。另外,由於要在slave上插入併發,也會遇到讀複製延遲問題。
  • 此外,還開發了一個公用服務框架:
    • 花了很多時間解決分散式系統管理這個運維問題。
    • 為服務開發了一種Rails scaffolding,內部用模板來啟動服務。
    • 所有服務從運維的角度來看都是一樣的,所有服務檢查統計資料、監控、啟動和停止的方式都一樣。
    • 工具方面,構建過程圍繞SBT(一個Scala構建工具),使用外掛和輔助程式管理常見操作,包括在Git裡打標籤,釋出到程式碼庫等等。大多數程式設計師都不用再操心構建系統的細節了。
  • 40臺伺服器採用HAProxy進行負載均衡,Varnish進行快取.
  • 200臺資料庫伺服器中,很多是為了提高可用性而設,使用的是常規硬體,但MTBF(平均故障間隔時間)極低。故障時,備用充足。
  • 500臺伺服器執行Apache和其他PHP程式。
  • 6個為了支援PHP應用的後端服務,並有一個小組專門開發後端服務。新服務的釋出需要兩到三週,包括Dashboard通知、Dashboard二級索引、短地址生成、處理透明分片的memcache代理。
  • 其中在MySQL分片上耗時很多。雖然在紐約本地非常熱,但並沒有使用MongoDB,他們認為MySQL的可擴充套件性足夠了。
  • Gearman用於會長期執行無需人工干預的工作。
  • 可用性是以達到範圍(reach)衡量的。使用者能夠訪問自定義域或者Dashboard嗎?也會用錯誤率。
  • 歷史上總是解決那些最高優先順序的問題,而現在會對故障模式系統地分析和解決,目的是從使用者和應用的角度來定成功指標。
  • 最開始Finagle是用於Actor模型的,但是後來放棄了。對於執行後無需人工干預的工作,使用任務佇列。而且Twitter的util工具庫中有Future實現,服務都是用Future(Scala中的無引數函式,在與函式關聯的並行操作沒有完成時,會阻塞呼叫方)實現的。當需要執行緒池的時候,就將Future傳入Future池。一切都提交到Future池進行非同步執行。
  • Scala提倡無共享狀態。由於已經在Twitter生產環境中經過測試,Finagle這方面應該是沒有問題的。使用Scala和Finagle中的結 構需要避免可變狀態,不使用長期執行的狀態機。狀態從資料庫中拉出、使用再寫回資料庫。這樣做的好處是,開發人員不需要操心執行緒和鎖。
  • 22臺Redis伺服器,每臺的都有8-32個例項,因此線上同時使用了100多個Redis例項。
    • Redis主要用於Dashboard通知的後端儲存。
    • 所謂通知就是指某個使用者like了某篇文章這樣的事件。通知會在使用者的Dashboard中顯示,告訴他其他使用者對其內容做了哪些操作。
    • 高寫入率使MySQL無法應對。
    • 通知轉瞬即逝,所以即使遺漏也不會有嚴重問題,因此Redis是這一場景的合適選擇。
    • 這也給了開發團隊瞭解Redis的機會。
    • 使用中完全沒有發現Redis有任何問題,社群也非常棒。
    • 開發了一個基於Scala Futures的Redis介面,該功能現在已經併入了Cell架構。
    • 短地址生成程式使用Redis作為一級Cache,HBase作為永久儲存。
    • Dashboard的二級索引是以Redis為基礎開發的。
    • Redis還用作Gearman的持久儲存層,使用Finagle開發的memcache代理。
    • 正在緩慢地從memcache轉向Redis。希望最終只用一個cache服務。效能上Redis與memcache相當。

內部通訊管道(Firehose)

  • 內部的應用需要活躍的資訊流通道。這些資訊包括使用者建立/刪除的資訊,liking/unliking 的提示,等等。挑戰在於這些資料要實時的分散式處理。我們希望能夠檢測內部執行狀況,應用的生態系統能夠可靠的生長,同時還需要建設分散式系統的控制中心。
  • 以前,這些資訊是基於 Scribe (Facebook 開源的分散式日誌系統。)/Hadoop 的分散式系統。服務會先記錄在 Scribe 中,並持續的長尾形式寫入,然後將資料輸送給應用。這種模式可以立即停止伸縮,尤其在峰值時每秒要建立數以千計的資訊。不要指望人們會細水長流式的釋出文 件和 grep。
  • 內部的 firehose 就像裝載著資訊的大巴,各種服務和應用透過 Thrift 與消防管線溝通。(一個可伸縮的跨語言的服務開發框架。)
  • LinkedIn 的 Kafka 用於儲存資訊。內部人員透過 HTTP 連結 firehose。經常面對巨大的資料衝擊,採用 MySQL 顯然不是一個好主意,分割槽實施越來越普遍。
  • firehose 的模型是非常靈活的,而不像 Twitter 的 firehose 那樣資料被假定是丟失的。
    • firehose 的資訊流可以及時的回放。他保留一週內的資料,可以調出這期間任何時間點的資料。
    • 支援多個客戶端連線,而且不會看到重複的資料。每個客戶端有一個 ID。Kafka 支援客戶群,每個群中的客戶都用同一個 ID,他們不會讀取重複的資料。可以建立多個客戶端使用同一個 ID,而且不會看到重複的資料。這將保證資料的獨立性和並行處理。Kafka 使用 ZooKeeper (Apache 推出的開源分散式應用程式協調服務。)定期檢查使用者閱讀了多少。

為 Dashboard 收件箱設計的 Cell 架構

  • 現在支援 Dashboard 的功能的分散-集中架構非常受限,這種狀況不會持續很久。
    • 解決方法是採用基於 Cell 架構的收件箱模型,與 Facebook Messages 非常相似。
    • 收件箱與分散-集中架構是對立的。每一位使用者的 dashboard 都是由其追隨者的發言和行動組成的,並按照時間順序儲存。
    • 就因為是收件箱就解決了分散-集中的問題。你可以會問到底在收件箱中放了些什麼,讓其如此廉價。這種方式將執行很長時間。
  • 重寫 Dashboard 非常困難。資料已經分佈,但是使用者區域性升級產生的資料交換的質量還沒有完全搞定。
    • 資料量是非常驚人的。平均每條訊息轉發給上百個不同的使用者,這比 Facebook 面對的困難還要大。大資料+高分佈率+多個資料中心。
    • 每秒鐘上百萬次寫入,5萬次讀取。沒有重複和壓縮的資料增長為2.7TB,每秒百萬次寫入操作來自 24 位元組行鍵。
    • 已經流行的應用按此方法執行。
  • Cell構架
    • 每個 cell 是獨立的,並儲存著一定數量使用者的全部資料。在使用者的 Dashboard 中顯示的所有資料也在這個 cell 中。
    • 使用者對映到 cell。一個資料中心有很多 cell。
    • 每個 cell 都有一個 HBase 的叢集,服務叢集,Redis 的快取叢集。
    • 使用者歸屬到 cell,所有 cell 的共同為使用者發言提供支援。
    • 每個 cell 都基於 Finagle(Twitter 推出的非同步的遠端過程呼叫庫),建設在 HBase 上,Thrift 用於開發與 firehose 和各種請求與資料庫的連結。
    • 一個使用者進入 Dashboard,其追隨者歸屬到特定的 cell,這個服務節點透過 HBase 讀取他們的 dashboard 並返回資料。
    • 後臺將追隨者的 dashboard 歸入當前使用者的 table,並處理請求。
    • Redis 的快取層用於 cell 內部處理使用者發言。
  • 請求流:使用者釋出訊息,訊息將被寫入 firehose,所有的 cell 處理這條訊息並把發言文字寫入資料庫,cell 查詢是否所有釋出訊息追隨者都在本 cell 內,如果是的話,所有追隨者的收件箱將更新使用者的 ID。
  • cell 構架的優點:
    • 大規模的請求被並行處理,元件相互隔離不會產生干擾。 cell 是一個並行的單位,因此可以任意調整規格以適應使用者群的增長。
    • cell 的故障是獨立的。一個 Cell 的故障不會影響其他 cell。
    • cell 的表現非常好,能夠進行各種升級測試,實施滾動升級,並測試不同版本的軟體。
  • 關鍵的思想是容易遺漏的:所有的發言都是可以複製到所有的 cell。
    • 每個 cell 中儲存的所有發言的單一副本。 每個 cell 可以完全滿足 Dashboard 呈現請求。應用不用請求所有發言者的 ID,只需要請求那些使用者的 ID。他可以在 dashboard 返回內容。每一個 cell 都可以滿足 Dashboard 的所有需求,而不需要與其他 cell 進行通訊。
    • 用到兩個 HBase table :一個 table 用於儲存每個發言的副本,這個 table 相對較小。在 cell 內,這些資料將與儲存每一個發言者 ID。第二個 table 告訴我們使用者的 dashboard 不需要顯示所有的追隨者。當使用者透過不同的終端訪問一個發言,並不代表閱讀了兩次。收件箱模型可以保證你閱讀到。
    • 發言並不會直接進入到收件箱,因為那實在太大了。所以,發言者的 ID 將被髮送到收件箱,同時發言內容將進入 cell。這個模式有效的減少了儲存需求,只需要返回使用者在收件箱中瀏覽發言的時間。而缺點是每一個 cell 儲存所有的發言副本。令人驚奇的是,所有發言比收件箱中的映象要小。每天每個 cell 的發言增長 50GB,收件箱每天增長2.7TB。使用者消耗的資源遠遠超過他們製造的。
    • 使用者的 dashboard 不包含發言的內容,只顯示發言者的 ID,主要的增長來自 ID。
    • 當追隨者改變時,這種設計方案也是安全的。因為所有的發言都儲存在 cell 中了。如果只有追隨者的發言儲存在 cell 中,那麼當追隨者改變了,將需要一些回填工作。
    • 另外一種設計方案是採用獨立的發言儲存叢集。這種設計的缺點是,如果群集出現故障,它會影響整個網站。因此,使用 cell 的設計以及後複製到所有 cell 的方式,建立了一個非常強大的架構。
  • 一個使用者擁有上百萬的追隨者,這帶來非常大的困難,有選擇的處理使用者的追隨者以及他們的存取模式(見Feeding Frenzy
    • 不同的使用者採用不同並且恰當的存取模式和分佈模型,兩個不同的分佈模式包括:一個適合受歡迎的使用者,一個使用大眾。
    • 依據使用者的型別採用不同的資料處理方式,活躍使用者的發言並不會被真正釋出,發言將被有選擇的體現。
    • 追隨了上百萬使用者的使用者,將像擁有上百萬追隨者的使用者那樣對待。
  • cell 的大小非常難於決定。cell 的大小直接影響網站的成敗。每個 cell 歸於的使用者數量是影響力之一。需要權衡接受怎樣的使用者體驗,以及為之付出多少投資。
  • 從 firehose 中讀取資料將是對網路最大的考驗。在 cell 內部網路流量是可管理的。
  • 當更多 cell 被增添到網路中來,他們可以進入到 cell 組中,並從 firehose 中讀取資料。一個分層的資料複製計劃。這可以幫助遷移到多個資料中心。

在紐約啟動運作

  • 紐約具有獨特的環境,資金和廣告充足。招聘極具挑戰性,因為缺乏創業經驗。
  • 在過去的幾年裡,紐約一直致力於推動創業。紐約大學和哥倫比亞大學有一些專案,鼓勵學生到初創企業實習,而不僅僅去華爾街。市長建立了一所學院,側重於技術。

團隊架構

  • 團隊:基礎架構,平臺,SRE,產品,web ops,服務
  • 基礎架構:5層以下,IP 地址和 DNS,硬體配置
  • 平臺:核心應用開發,SQL 分片,服務,Web 運營
  • SRE:在平臺和產品之間,側重於解決可靠性和擴充套件性的燃眉之急
  • 服務團隊:相對而言更具戰略性
  • Web ops:負責問題檢測、響應和最佳化

軟體部署

  • 開發了一套 rsync 指令碼,可以隨處部署 PHP 應用程式。一旦機器的數量超過 200 臺,系統便開始出現問題,部署花費了很長時間才完成,機器處於部署程式中的各種狀態。
  • 接下來,使用 Capistrano(一個開源工具,可以在多臺伺服器上執行指令碼)在服務堆疊中構建部署程式(開發、分期、生產)。在幾十臺機器上部署可以正常工作,但當透過 SSH 部署到數百臺伺服器時,再次失敗。
  • 現在,所有的機器上執行一個協調軟體。基於 Redhat Func(一個安全的、指令碼化的遠端控制框架和介面)功能,一個輕量級的 API 用於向主機傳送命令,以構建擴充套件性。
  • 建立部署是在 Func 的基礎上向主機傳送命令,避免了使用 SSH。比如,想在組A上部署軟體,控制主機就可以找出隸屬於組A的節點,並執行部署命令。
  • 透過Capistrano的命令實現部署。它可以可以從git倉庫中拉取程式碼。正因為是基於HTTP的,所以非常易於擴充套件。他們喜歡 Capistrano,因為它支援簡單的基於目錄的版本控制,能夠很好地管理PHP程式。版本更新的時候,每個目錄都包含一個SHA,所以很容易檢查版本 的正確性。
  • Func API 可用於返回狀態報告,報告哪些機器上有這些軟體版本。
  • 安全重啟任何服務,因為它們會關閉連線,然後重啟。
  • 在啟用前的黑暗模式下執行所有功能。

開發

  • 從哲學上,任何人都可以使用自己想要的任意工具。但隨著團隊的發展壯大,這些工具出現了問題。新員工想要更好地融入團隊,快速地解決問題,必須以他們為中心,建立操作的標準化。
  • 過程類似於 Scrum(一種敏捷管理框架),非常敏捷。
  • 每個開發人員都有一臺預配置的開發機器,並按照控制更新。
  • 開發機會出現變化,測試,分期,乃至用於生產。
  • 開發者使用 VIM 和 TextMate。
  • 測試是對 PHP 程式進行程式碼稽核。
  • 在服務方面,他們已經實現了一個與提交相掛鉤的測試基礎架構,接下來將繼承並內建通知機制。

招聘流程

  • 面試通常避免數學、猜謎、腦筋急轉彎等問題,而著重關注應聘者在工作中實際要做什麼。
  • 著重程式設計技能。
  • 面試不是比較,只是要找對的人。
  • 挑戰在於找到具有可用性、擴充套件性經驗的人才,以應對 Tumblr 面臨的網路擁塞。
    • 例如,對於一個新的ID生成器器,他們需要一個高可用的JVM程式,並且需要在1萬次每分鐘的請求下及最多使用500兆記憶體的情況下完成1毫秒以下的響應。他們發現序列收集器給這個特定的工作負載帶來最低的延遲。在 JVM最佳化上花了大量的時間。
  • 在 Tumblr 工程部落格(Tumblr Engineering Blog),他們對已過世的 Dennis Ritchie 和 John McCarthy 予以紀念。

經驗及教訓

  • 自動化無處不在
  • MySQL(增加分片)規模,應用程式暫時還不行
  • Redis 總能帶給人驚喜
  • 基於 Scala 語言的應用執行效率是出色的
  • 廢棄專案——當你不確定將如何工作時
  • 不顧用在他們發展經歷中沒經歷過技術挑戰的人,聘用有技術實力的人是因為他們能適合你的團隊以及工作。
  • 選擇正確的軟體集合將會幫助你找到你需要的人
  • 建立團隊的技能
  • 閱讀文件和部落格文章。
  • 多與同行交流,可以接觸一些領域中經驗豐富的人,例如與在 Facebook、Twitter、LinkedIn 的工程師多交流,從他們身上可以學到很多
  • 對技術要循序漸進,在正式投入使用之前他們煞費苦心的學習 HBase 和 Redis。同時在試點專案中使用或將其控制在有限損害範圍之內。

原文:http://highscalability.com/blog/2013/5/20/the-tumblr-architecture-yahoo-bought-for-a-cool-billion-doll.html

譯文:http://www.oschina.net/translate/the-tumblr-architecture-yahoo-bought-for-a-cool-billion-doll

參與翻譯:_Raymond, 紅薯

Tumblr 架構設計

相關文章