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