大概是2009年,我和兩個好哥們聊天,覺得智能手機可能是風口,商量着要弄一個照片分享網站。
用戶可以用手機把隨手拍的照片放到網上分享,名稱都起好了,叫InstantPost。
可是我們的執行力太差了,聚了兩次,做了一點兒技術驗證,就沒有下文了。
過了幾年,我看到美國一個叫Instagram的火了,不由地一拍大腿:臥槽!這不就是我們當年要做的事兒嗎?!

後來我看到Instagram初期的故事,他們也是三個程式設計師,從2010年10月到2011年12月,在一年多的時間內,就把用戶數量從0增長到了1400萬!
看完他們的架構設計,我就釋然了,拋開執行力,在2009年那個時間點,我們確實不行。
Instagram制定的架構指導準則是:
1.保持簡單
2.不要重新發明輪子
3.儘可能使用經過驗證的可靠技術
所以早期的Instagram跑在雲上,使用EC2和Ubuntu Linux11.04。
接下來,站在一個用戶會話(Session)的角度,來看看Instagram的處理過程。
前端
Session:用戶打開了Instagram APP。
2010年,Instagram開發了一個iOS app,正式推出。
因為這時候Swift還沒有發佈,他們用了Objective-C,UIKit等技術。

負載均衡
Session:打開App後,會向後端發起一個請求(獲取主界面的「信息流」),這個請求會首先到達Instagram的負載均衡。
Instagram最早使用2個Nginx並在它們之間進行DNS Round-Robin,這種方法的缺點是,如果某一個機器出現故障,DNS的更新需要時間。
後來他們選擇了Amazon的Elastic Load Balancer,這裏有三個NGINX實例,可以換入換出。

後端
Session:負載均衡會把請求轉發給應用伺服器
Instagram用Django作為後端服務,運行在 Amazon High-CPU Extra-Large上,因為這三個程式設計師發現,後端服務是CPU密集型的。
用Gunicorn做WSGI Server。

應用運行在超過25台亞馬遜虛擬機中,這些應用都是無狀態的,可以在需要的時候進行擴展。
為了在多台機器上運行命令(例如部署代碼),Instagram使用了Fabric,它有一個很好用的並行模式,部署只需要幾秒鐘。
數據存儲
Session:用戶請求到達了應用伺服器,接下來它需要獲得這些數據:
1.最新的Photo IDs
2.這些Photo ID對應的實際照片
3.這些照片的用戶數據
Database: PostgreSQL
Session:應用伺服器從PostgreSQL獲取最新的Photo ID,這裏保存着用戶和照片的元數據。
大部分的數據,如用戶,照片元數據,標籤等都保存在PostgreSQL數據庫中。
因為數據量不小,每秒鐘有25個照片上傳,並且有90個贊,Instagram對數據做了分片。
分片系統由數千個邏輯分片組成,這些分片在代碼中被映射到少得多的物理分片,用這種辦法,可以從少量的數據庫開始,擴展到更多的數據庫。
當擴展時,只需要把邏輯分片從一個數據庫「指向」另外一個即可,無需挪動任何數據。
一個挑戰是:Instagram如何解決Photo ID問題,因為需要能按時間排序,而無需獲得有關照片的更多信息,理想情況下,ID應該是64位的。
後來的解決方案是這樣的:
41位:記錄毫秒時間
13位:邏輯分片ID
10位:自動增長的序列,模數1024,這意味着每毫秒,每個分片可以生成1024個ID
最終結果是個64為整數,可以被PostgreSQL排序,找到最新的照片。
照片的存儲:S3和Cloudfront
Session:獲取了Photo ID以後,應用伺服器要獲取真正的照片,快速發給用戶
照片保存在Amazon S3中,存儲了幾個TB的數據,通過使用CDN(Amazon CloudFront),照片可以快速分發給世界各地的用戶(例如日本,是Instagram第二大受歡迎的國家)
緩存
Instagram需要將大約3億張照片(ID)和創建它們的用戶ID的映射保存起來,以便知道查詢那個分片。
他們選擇了Redis後發現,為了保存這些映射,Redis需要21GB內存,這已經大於 Amazon EC2上的17GB實例類型。
後來他們向Redis的核心開發人員Pieter Noordhuis求助,Pieter建議使用Redis Hash,最終通過巧妙的設計,這些映射僅需不到5G的內存。
對於其他緩存(如session),Instagram使用Memcached,當時有6個實例。

數據備份
無論是PostGreSQL還是Redis,都會使用Amazon EBS經常性進行數據備用
通知和異步任務
Session:用戶關閉了App,但是朋友發送了一張照片,需要發送一個通知。
Instagram的推送服務用的是 pyapns,這是一個開源的、通用的蘋果推送服務提供商,運行非常穩定,為Instagram處理了超過10億條推送通知。
Session:用戶非常喜歡這張照片!他決定在Twitter何Facebook上分享。
在後台,任務被推送到了Gearman,這個任務隊列會保存任務,Instagram有大約200 Python workers來處理這些任務。

監控
Session: Uh oh!伺服器端發生了錯誤,Instagram崩潰了,那三個程式設計師需要收到告警,馬上進行處理。
Instagram使用 Sentry這個開源的應用來實時監控Python錯誤。
使用Munin來繪製各種系統指標的圖表,如果有任何情況超出正常範圍,就會向程式設計師發出異常告警。Instagram有一堆自定義 Munin插件來跟蹤應用程式級別的指標,例如每秒發佈的照片、每分鐘註冊人數等。
對於外部服務的監控,使用了Pingdom,PagerDuty用於處理事件和通知。
最終的架構

反思
2009年,我們三個都在比較傳統的軟件公司,互聯網技術用得比較少。
像負載均衡、分庫分表、緩存也是剛剛開始接觸,還沒有在生產系統中大規模使用的經驗。
iPhone還沒在國內上市,我們仨手頭都沒有,還在用諾基亞的「智能機」做測試。
國內市場也沒有很好的雲服務作為基礎設施,當時李彥宏表示,雲計算只不過是新瓶裝舊酒,15年來沒有新東西,馬化騰則認為雲計算要像水電一樣用還為時尚早。
如果執行力強的話,InstantPost應該能做出來,但肯定會遇到很多的坑,想取得一年1000多萬用戶肯定是痴心妄想。
當然,這僅僅說明是我們三個比較菜,不是中國程式設計師不行,中國程式設計師在互聯網時代也創造了很多優秀的產品,甚至殺到了美國大本營。
我想說的是,很多看起來是風口的東西,我們是抓不住的,因為:
我們不是局內人。


















