整理 | 云昭
策劃 | 言征
出品 | 51CTO技術棧(微信號:blog51cto)
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的自學成才的歷程成為了激烈開發者們對于編程的熱情。
參考鏈接:
https://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram