本文翻譯自How Twitter Handles 3,000 Images Per Second,作者是Todd Hoff,翻譯已獲得本人授權(quán)。
今天的Twitter每秒鐘能夠創(chuàng)建并保存3000張(200GB)圖片。甚至早在2015年,Twitter就通過改進媒體存儲策略節(jié)約了6百萬美元。
以前并不是這樣的。Twitter在2012年主要以提供文字信息為主,當時的用戶也很少發(fā)布各種炫酷的動態(tài)圖片。到現(xiàn)在2016年,Twitter開始提供富媒體功能。這樣的轉(zhuǎn)變是通過Twitter自行開發(fā)的全新媒體平臺(Media Platform)實現(xiàn)的,這個平臺可以支持照片預(yù)覽、多張照片發(fā)送、動態(tài)Gif圖片、Vine視頻,以及內(nèi)嵌視頻等功能。
Twitter軟件開發(fā)工程師Henna Kermani在Mobile @Scale London活動中,通過一場名為每秒3000張圖片的有趣演講介紹了Twitter的媒體平臺。演講主要側(cè)重于圖像處理方面,但她講的大部分細節(jié)也同樣適用于其他形式的媒體。
演講中一些比較有趣的重點包括:
因陋就簡的處理方法注定將會失敗。在原本不支持的情況下,不假思索通過簡單的手段支持上傳帶圖片的推文,這樣的做法會造成某種形式的“套牢”。并且這種方式缺乏縮放能力,尤其是在網(wǎng)絡(luò)狀況不佳的時候,這使得Twitter很難增加新的功能。 去耦合。通過將推文與所包含的媒體去耦合,Twitter可以分別針對每個途徑進行優(yōu)化,在運營方面獲得更大程度的靈活性。 移動句柄,而非存儲塊。不要在您的系統(tǒng)內(nèi)部移動過大的數(shù)據(jù)塊。這種做法將消耗大量帶寬,會導(dǎo)致所有需要訪問這些數(shù)據(jù)的服務(wù)遭遇性能問題。更好的方法是單獨存儲數(shù)據(jù),并通過句柄的方式進行引用。 使用分塊可續(xù)傳方式上傳可大幅降低媒體文件的上傳失敗率。 持續(xù)的實驗和研究。Twitter研究發(fā)現(xiàn),對于圖片的不同變體(例如縮略圖、小圖、大圖等),20天的存活時間(Time to live, TTL)是最佳甜區(qū),這個值可以在存儲和計算之間實現(xiàn)良好的平衡。圖片內(nèi)容發(fā)布20天之后的訪問概率會大幅降低,因此可以刪除圖片的各種變體,這樣的做法每天可以幫助Twitter節(jié)約4TB數(shù)據(jù)存儲空間,并將需要的計算服務(wù)器數(shù)量減少幾乎一半,同時每年可節(jié)約數(shù)百萬美元。 按需。老圖片的不同變體可以放心刪除,并在需要時重建,而無須預(yù)先創(chuàng)建好。這種按需執(zhí)行服務(wù)的做法可改善靈活性,讓您更清楚任務(wù)的執(zhí)行方式,并對其進行集中的控制。 漸顯式JPEG(Progressive JPEG)作為一種標準圖片格式無疑是真正的贏家。這種格式在前端和后端都有良好的支持,在速度不快的網(wǎng)絡(luò)中也能提供不錯的表現(xiàn)。Twitter開發(fā)富媒體功能的過程中還發(fā)生了一些很棒的故事,一起來學(xué)學(xué)他們是如何做到的...
舊途 - 2012年的Twitter寫入路徑用戶在某個應(yīng)用中撰寫了一條推文,并可能在推文中附加了一張圖片。
客戶端將該推文發(fā)送至一個單體端點(Monolithic endpoint)。圖片會作為附帶內(nèi)容與推文的所有其他元數(shù)據(jù)一起上傳,并傳遞至這一過程涉及到的每個服務(wù)。 在原本的設(shè)計下,這個端點是造成很多問題的根源。問題1:浪費大量網(wǎng)絡(luò)帶寬
推文的創(chuàng)建和媒體的上傳緊密耦合為一個操作。
相關(guān)廠商內(nèi)容
滴滴出行iOS客戶端架構(gòu)演進之路! 微信客戶端如何應(yīng)對弱網(wǎng)絡(luò)! 函數(shù)式編程中的Swift與Swift中的函數(shù)式編程! 你離成為一位合格的技術(shù)領(lǐng)導(dǎo)者還有多遠? 國際范 最前沿 不容錯過的容器技術(shù)盛會相關(guān)贊助商
GMTC全球移動技術(shù)大會2016年6月24日-25日,北京,點擊了解詳情!
上傳過程充滿不確定性,或者完全成功或者徹底失敗。失敗可能出于任何原因,例如網(wǎng)絡(luò)卡頓或傳輸錯誤等,如果失敗就只能從頭開始重新執(zhí)行整個上傳過程,包括上傳圖片。例如,假設(shè)上傳操作在進行到95%后出錯,也需要將所有內(nèi)容重新上傳一遍。
問題2:面對新出現(xiàn)更大體積的媒體無法很好地縮放
這種方式無法通過縮放支持視頻等更大體積的媒體文件。體積的增大會導(dǎo)致出錯概率增加,尤其是在諸如巴西、印度、印度尼西亞這樣網(wǎng)絡(luò)速度慢并且不可靠的新興市場,而Twitter非常迫切地希望提高這些地區(qū)用戶上傳推文的成功率。問題3:內(nèi)部帶寬使用效率低下
端點需要連接至負責(zé)處理用戶身份驗證和路由的Twitter前端(TFE),隨后用戶會被路由至某個圖片服務(wù)(Image Service)。
圖片服務(wù)聯(lián)系變體生成器(Variant Generator),用不同尺寸(例如小、中、大、縮略圖)為圖片生成不同實例。這些變體會存儲在BlobStore中,這是一種專為圖片和視頻等大型載荷進行過優(yōu)化的鍵值(key-value)類型存儲。隨后圖片將永遠存儲在這里。
在創(chuàng)建和保存推文的過程中,還涉及到很多其他服務(wù)。因為端點是單體的,如果將媒體與推文元數(shù)據(jù)結(jié)合在一起,就需要在所有服務(wù)之間傳輸這種綁定在一起的數(shù)據(jù)。這樣的大型載荷甚至?xí)鬟f到原本在設(shè)計上并不用于直接處理圖片的服務(wù),這些服務(wù)甚至并不是媒體傳遞渠道的一部分,但依然需要針對大型載荷的處理進行優(yōu)化。這樣的做法導(dǎo)致內(nèi)部帶寬使用效率嚴重降低。
問題4:存儲容量極度膨脹
數(shù)月甚至數(shù)年前發(fā)布的推文中所包含的圖片已無人問津,但依然需要永遠存儲在BlobStore中,這些內(nèi)容耗費了寶貴的存儲空間。因為沒有垃圾回收機制,有時甚至在推文被刪除后,圖片依然會保存在BlobStore中。讀取路徑用戶看到一條包含圖片的推文。圖片來自哪里?
客戶端從CDN請求圖片的變體。CDN可能需要從原始位置或TFE處檢索該圖片。這一過程最終導(dǎo)致需要通直接查詢BlobStore的方式,使用URL請求某一特定尺寸的圖片。
問題5:無法引入新的變體
這種設(shè)計不是非常靈活。如果需要添加新的變體,也就是說需要為圖片創(chuàng)建一個新的尺寸,此時必須為BlobStore中的每個圖片創(chuàng)建一個新尺寸的版本。這種方式缺乏按需創(chuàng)建變體的便利機制。
缺乏靈活性意味著Twitter很難為客戶端添加新的功能。
新法 - 2016年的Twitter寫入路徑將上傳的媒體與推文去耦合。
上傳操作至此成為“一等公民”,并創(chuàng)建了專門用于將原始圖片存儲至BlobStore的上傳端點。
這種方法為上傳操作的處理提供了極大的靈活性。
客戶端聯(lián)系TFE,TFE隨后聯(lián)系圖片服務(wù),圖片服務(wù)將圖片保存至BlobStore并將相關(guān)數(shù)據(jù)添加至一個元數(shù)據(jù)存儲。僅此而已。這一過程不會涉及任何隱藏的服務(wù),不需要處理媒體,也不需要四處傳遞圖片。
隨后圖片服務(wù)會返回一個代表該媒體的唯一標識符,即mediaId。當客戶端需要創(chuàng)建推文,發(fā)布私信,或更新自己的頭像照片時,將使用這個mediaId作為引用該媒體的句柄,而不需要提供媒體的原始文件。
假設(shè)用戶想要使用剛上傳的圖片發(fā)布推文,過程將會是這樣:
客戶端聯(lián)系更新端點,在推文中包含mediaId,該請求將發(fā)送至Twitter前端,隨后TFE會將請求路由至對于所創(chuàng)建內(nèi)容來說最為恰當?shù)姆?wù)。對推文本身,最適宜的服務(wù)是TweetyPie,私信和用戶資料信息的處理也由不同服務(wù)進行,所有這些服務(wù)都能與圖片服務(wù)通信,圖片服務(wù)器中包含處理面孔檢測、兒童色情內(nèi)容檢測等功能所需的推文處理隊列,當這些任務(wù)執(zhí)行完畢后,圖片服務(wù)會與處理圖片的ImageBird或處理視頻的VideoBird服務(wù)通信。ImageBird負責(zé)生成變體,VideoBird則對視頻進行一定的轉(zhuǎn)碼,最終處理生成的媒體內(nèi)容將保存至BlobStore。
不再需要將媒體內(nèi)容四處傳輸,借此可節(jié)約大量本被浪費的帶寬。
分塊可續(xù)傳的上傳。
進入地鐵站,10分鐘后出來,上傳過程可從上次中斷的地方恢復(fù)進行。對用戶來說該過程是完全無縫的。
客戶端使用上傳API發(fā)起上傳會話,后端會為用戶提供一個mediaId,這個mediaId將在整個上傳會話中充當標識符。
圖片被拆分為多個小塊,例如拆分成三塊。這些文件塊可使用API附加到一起,每次調(diào)用的附加操作可提供必要的片段索引,所有附加操作都可作用于同一個mediaId。上傳完成后對上傳內(nèi)容進行“定稿”,隨后這個媒體就可以使用了。
這種方法更容易適應(yīng)網(wǎng)絡(luò)故障。每個獨立小塊可以重試,如果網(wǎng)絡(luò)因為任何原因中斷,用戶可以暫停并在網(wǎng)絡(luò)恢復(fù)后從暫停的位置繼續(xù)上傳。
方法簡單,效益巨大。對于體積超過50KB的文件,上文提到的三個國家中圖片上傳失敗率降低幅度分別為:巴西,33%;印度,30%;印度尼西亞,19%。
讀取路徑此處用到了一種名為MinaBird的CDN源服務(wù)器。
MinaBird可與ImageBird和VideoBird通信,這樣就算不存在,也可以即時生成不同尺寸圖片和不同格式視頻的變體。
在處理客戶端請求方面,MinaBird更流暢也更動態(tài)。舉例來說,假設(shè)有內(nèi)容由于DMCA(數(shù)字千年版權(quán)法)的要求需要刪除,此時很容易便可阻止對相關(guān)內(nèi)容的訪問,或重新允許對媒體特定片段的訪問。
按需即時生成變體和轉(zhuǎn)碼的方式使得Twitter在存儲容量的使用方面更為高效。
按需生成變體,意味著不需要將所有變體都存儲在BlobStore中,這是一個巨大的進步。
原始圖片在刪除前將一直保留,而變體只保留20天。媒體平臺團隊針對最佳過期時限進行了大量研究,發(fā)現(xiàn)在所有請求的圖片中,有大約50%的圖片都是在最多15天(左右)的時間內(nèi)上傳的。繼續(xù)保留更早前上傳的圖片可以獲得的收益在逐漸下降。更老的媒體文件也有可能就此無人問津。15天后存在一條很長的長尾。
不設(shè)置存活時間(TTL)并且不過期的情況下,媒體文件的存儲導(dǎo)致數(shù)據(jù)存儲總量每天增加6TB,按需生成所有變體的“偷懶”做法會讓數(shù)據(jù)存儲總量每天增加1.5TB。20天存活時間所用的存儲空間并不像“偷懶”做法那么多,因此在存儲方面的成本并不高,但對計算的要求更高了。對于“偷懶”的做法,在讀取的同時生成所有變體,需要為每個數(shù)據(jù)中心提供150臺ImageBird服務(wù)器,而20天存活時間的做法只需要投入75臺。因此20天存活時間是一個甜區(qū),可以在存儲和計算方面實現(xiàn)平衡。
由于節(jié)約存儲和計算資源等同于省錢,通過采取20天存活時間的做法,Twitter在2015年節(jié)約了6百萬美元。
客戶端的改進(Android)針對Google創(chuàng)建的圖像格式WebP執(zhí)行了為期6個月的實驗。
相比PNG或JPEG圖片,這種格式的圖片體積平均減小25%。
用戶參與積極性有所提高,尤其是在減小圖片體積可以幫助網(wǎng)絡(luò)減壓的新興市場。
iOS 不支持該格式。
僅 Android 4.0 以上系統(tǒng)可支持。
平臺支持的缺乏使得WebP的支持代價不菲。
Twitter還嘗試過漸顯式JPEG。這種格式可以使用逐行掃描的方式進行渲染,首次掃描的圖片可能顯得斑駁不勻,但可通過逐行掃描的方式逐漸進行完善。
性能更好。
后端易于支持。
相比傳統(tǒng)JPEG編碼速度慢60%。但由于編碼工作只需要進行一次,隨后所有用戶都可從中受益,因此這不算什么大問題。
不支持透明,因此還需保留透明PNG,但漸顯式JPEG其他方面都很出色。
客戶端對該格式的支持是通過Facebook的Fresco庫實現(xiàn)的。Fresco庫的價值很值得大書特書,就算在2G網(wǎng)絡(luò)中也能實現(xiàn)讓人印象深刻的效果。PJPEG的首次掃描只產(chǎn)生10kb流量,因此很快就可以加載完成。當原生渠道還在等待加載,無法顯示任何內(nèi)容的時候,PJPEG已經(jīng)可以提供可分辨的圖片。
通過對推文詳細信息視圖的加載進行持續(xù)的實驗發(fā)現(xiàn),p50加載時間降低9%,p95加載時間降低27%,出錯率降低了74%。網(wǎng)絡(luò)速度緩慢的用戶無疑能從中獲得不菲的收益。