整理 | 云昭
策劃 | 言征
出品 | 51CTO技術(shù)棧(微信號(hào):blog51cto)
2010 年 10 月 6 日,舊金山。當(dāng)人們還沉浸在擁有更強(qiáng)大攝像頭的 iPhone 4 的激動(dòng)心情時(shí),一款名為“ Instagram ” 的 iOS 照片分享應(yīng)用出現(xiàn)在應(yīng)用商店中。
當(dāng)天就收獲了 2.5 萬首批用戶,一周之后,下載量攀升到 10 萬,從 2010 年 10 月到 2011 年 12 月,Instagram在短短一年多的時(shí)間里,用戶數(shù)量從 0 增長(zhǎng)到 1400 萬。
而他的創(chuàng)始人, Kevin Systrom 這期間用了 3 名工程師就做到了這一點(diǎn)。這就讓我們回到那個(gè)奇幻的時(shí)刻,從工程師的角度思考,一探他們是如何做到的。
簡(jiǎn)單來說,他們通過如下遵循 3 個(gè)關(guān)鍵指導(dǎo)原則并擁有可靠的技術(shù)堆棧來做到這一點(diǎn):讓事情變得非常簡(jiǎn)單、不要重新發(fā)明輪子、盡可能使用經(jīng)過驗(yàn)證的可靠技術(shù)。
一、早期基礎(chǔ)配置
早期 Instagram 的基礎(chǔ)設(shè)施,是在 AWS 上運(yùn)行的,使用 EC2 和 Ubuntu Linux。作為參考,EC2 是亞馬遜的服務(wù),允許開發(fā)人員租用虛擬計(jì)算機(jī)。
為了讓事情變得簡(jiǎn)單,并且由于我喜歡從工程師的角度思考用戶,所以讓我們回顧一下用戶場(chǎng)景會(huì)話的生命周期。
二、前端
場(chǎng)景回顧:用戶打開界面。
Instagram 最初于 2010 年作為 iOS 應(yīng)用程序推出。由于 Swift 于 2014 年發(fā)布,我們可以假設(shè) Instagram 是使用Objective-C 和 UIKit 等其他東西的組合編寫的。
圖片
三、負(fù)載均衡
場(chǎng)景回顧:打開應(yīng)用程序后,獲取主訂閱源照片的請(qǐng)求會(huì)發(fā)送到后端,并在那里到達(dá)Instagram的負(fù)載均衡器。
Instagram 使用亞馬遜的彈性負(fù)載均衡器。他們有 3 個(gè) NGINX 實(shí)例,根據(jù)它們是否健康來?yè)Q入和換出。
每個(gè)請(qǐng)求首先到達(dá)負(fù)載均衡器,然后再路由到實(shí)際的應(yīng)用程序服務(wù)器。
圖片
四、后端
場(chǎng)景回顧:負(fù)載均衡器將請(qǐng)求發(fā)送到應(yīng)用程序服務(wù)器,應(yīng)用程序服務(wù)器保存正確處理請(qǐng)求的邏輯。
Instagram 的應(yīng)用服務(wù)器使用Django ,用 Python 編寫,Gunicorn作為他們的 WSGI 服務(wù)器。
回顧一下,WSGI(Web 服務(wù)器網(wǎng)關(guān)接口)將請(qǐng)求從 Web 服務(wù)器轉(zhuǎn)發(fā)到 Web 應(yīng)用程序。
Instagram 使用Fabric同時(shí)在多個(gè)實(shí)例上并行運(yùn)行命令。這允許在幾秒鐘內(nèi)部署代碼。
它們運(yùn)行在超過 25 臺(tái) Amazon High-CPU 超大型機(jī)器上。由于服務(wù)器本身是無狀態(tài)的,當(dāng)他們需要處理更多請(qǐng)求時(shí),他們可以添加更多機(jī)器。
圖片
五、通用數(shù)據(jù)存儲(chǔ)
場(chǎng)景回顧:應(yīng)用服務(wù)器發(fā)現(xiàn)請(qǐng)求需要主提要的數(shù)據(jù)。為此,我們假設(shè)它需要:
- 最新的相關(guān)照片ID
- 與這些照片ID匹配的實(shí)際照片
- 這些照片的用戶數(shù)據(jù)。
1.數(shù)據(jù)庫(kù):Postgres
場(chǎng)景回顧: 應(yīng)用服務(wù)器從Postgres獲取最新的相關(guān)照片ID。
應(yīng)用程序服務(wù)器將從PostgreSQL中提取數(shù)據(jù),PostgreSQL 存儲(chǔ)了 Instagram 的大部分?jǐn)?shù)據(jù),例如用戶和照片元數(shù)據(jù)。
Postgres 和 Django 之間的連接使用Pgbouncer進(jìn)行池化。
Instagram由于收到的數(shù)據(jù)量很大(每秒超過 25 張照片和 90 個(gè)贊)而對(duì)他們的數(shù)據(jù)進(jìn)行了分片。他們使用代碼將數(shù)千個(gè)“邏輯”分片映射到幾個(gè)物理分片。
Instagram 面臨并解決的一個(gè)有趣的挑戰(zhàn)是生成可以按時(shí)間排序的 ID。他們生成的可按時(shí)間排序的 ID 如下所示:
- 41 位時(shí)間(以毫秒為單位)(提供了 41 年的 ID 和自定義紀(jì)元)
- 13位代表邏輯分片ID
- 10 位表示自動(dòng)遞增序列,模數(shù) 1024。這意味著我們可以為每個(gè)分片、每毫秒生成 1024 個(gè) ID
場(chǎng)景回顧:由于Postgres中的可按時(shí)間排序的ID,應(yīng)用服務(wù)器已成功接收到最新的相關(guān)照片ID。
2.照片存儲(chǔ):S3 和 Cloudfront
場(chǎng)景回顧: 然后,應(yīng)用服務(wù)器通過快速CDN鏈接獲取與這些照片ID匹配的實(shí)際照片,以便為用戶快速加載。
Amazon S3中存儲(chǔ)了數(shù) TB 的照片。這些照片已使用 Amazon CloudFront快速提供給用戶。
3.緩存:Redis 和 Memcached
場(chǎng)景思考:為了從Postgres獲取用戶數(shù)據(jù),應(yīng)用服務(wù)器(Django)使用Redis將照片ID與用戶ID進(jìn)行匹配。
Instagram 使用 Redis 將大約 3 億張照片存儲(chǔ)到創(chuàng)建它們的用戶 ID 的映射,以便知道在獲取主提要、活動(dòng)提要等照片時(shí)要查詢哪個(gè)分片。所有 Redis 都存儲(chǔ)在內(nèi)存中減少延遲,并且它被分片到多臺(tái)機(jī)器上。
通過一些巧妙的哈希處理,Instagram 能夠在不到 5 GB 的空間中存儲(chǔ) 3 億個(gè)鍵映射。為了知道要查詢哪個(gè) Postgres 分片,需要此 photoID 到用戶 ID 鍵值映射。
場(chǎng)景回顧:由于使用Memcached的高效緩存,從Postgres獲取用戶數(shù)據(jù)很快,因?yàn)樽罱捻憫?yīng)被緩存了。
對(duì)于一般緩存,Instagram 使用Memcached 。他們當(dāng)時(shí)有 6 個(gè) Memcached 實(shí)例。Memcached 在 Django 上分層相對(duì)簡(jiǎn)單。
有趣的是:兩年后,即 2013 年,F(xiàn)acebook 發(fā)布了一篇具有里程碑意義的論文,介紹了他們?nèi)绾螖U(kuò)展 Memcached 以幫助他們每秒處理數(shù)十億個(gè)請(qǐng)求。
用戶現(xiàn)在可以看到主頁(yè),里面有他關(guān)注的人的最新照片。
圖片
4.主副本設(shè)置
Postgres 和 Redis 都在主副本設(shè)置中運(yùn)行,并使用 Amazon EBS(彈性塊存儲(chǔ))快照來頻繁備份系統(tǒng)。
六、推送通知和異步任務(wù)
場(chǎng)景回顧:現(xiàn)在,假設(shè)用戶關(guān)閉了應(yīng)用程序,但隨后收到朋友發(fā)布照片的推送通知。
此推送通知是使用pyapns發(fā)送的,與 Instagram 已經(jīng)發(fā)送的其他十億多條推送通知一起。Pyapns 是一個(gè)開源、通用的 Apple 推送通知服務(wù) (APNS) 提供商。
場(chǎng)景回顧:用戶非常喜歡這張照片!所以他決定在Twitter上分享。
在后端,任務(wù)被推送到Gearman中,這是一個(gè)任務(wù)隊(duì)列,它將工作外包給更適合的機(jī)器。Instagram 有大約 200 個(gè) Python 工作線程使用 Gearman 任務(wù)隊(duì)列。
Gearman 用于執(zhí)行多個(gè)異步任務(wù),例如向所有用戶的關(guān)注者推送活動(dòng)(例如發(fā)布的新照片)(這稱為扇出)。
圖片
七、監(jiān)控
場(chǎng)景回顧:Instagram應(yīng)用程序因服務(wù)器出現(xiàn)錯(cuò)誤而崩潰,并發(fā)送了錯(cuò)誤響應(yīng)。Instagram的三名工程師立即得到了提醒。
Instagram 使用Sentry (一款開源 Django 應(yīng)用程序)來實(shí)時(shí)監(jiān)控 Python 錯(cuò)誤。
Munin用于繪制系統(tǒng)范圍的指標(biāo)并發(fā)出異常警報(bào)。Instagram 有一堆自定義 Munin 插件來跟蹤應(yīng)用程序級(jí)別的指標(biāo),例如每秒發(fā)布的照片。
Pingdom用于外部服務(wù)監(jiān)控,PagerDuty用于處理事件和通知。
八、最終架構(gòu)一覽
圖片
——后記——
Instagram發(fā)布的19個(gè)月,后活躍用戶數(shù)量超過5000萬,活躍用戶數(shù)量達(dá)到1億,2012年6月份達(dá)到1.3億。同年10月25日,當(dāng)時(shí)的Facebook以總值7.15億美元收購(gòu)Instagram,創(chuàng)始人Kevin從中獲得了4億美元的回報(bào)。
值得一提的是,于編程而言,Kevin是自學(xué)成才。管理學(xué)科出身的他,剛畢業(yè)時(shí)可謂是一張白紙,在社交旅游網(wǎng)站Nextstop營(yíng)銷部門工作時(shí),凱文開始每晚抽時(shí)間自學(xué)編程。
Instagram 的成功帶來的不僅僅創(chuàng)造了當(dāng)代硅谷最偉大的成功故事之一,Kevin的自學(xué)成才的歷程成為了激烈開發(fā)者們對(duì)于編程的熱情。
參考鏈接:
https://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram