iDoneThis 架構剖析

之前為大家介紹了 iDoneThis 這個 service,今天讓我們來看看iDoneThis 的架構還有他們從剛開始到目前的開發心得吧。

一些數據

  • 每天收到 10,000 封 e-mail
  • 每天寄出 40,000 封 e-mail,大多數都是在美國時間的下午六點寄出
  • 每秒鐘數個 web-request
  • 1GB 的使用者資料,5GB 的資料庫
  • 文件有做即時索引和搜尋
  • 所有的 service 只用了一台 xlarge EC2 instance

架構

  • 幾乎所有事情都用 Python 搞定
  • Django 當做 web interface
  • apache mod_wsgi 來處理 web server
  • postgresql 作為資料庫
  • 收到的 email 經由 sendgrid parse API 跑過後再用自己寫的 email processor 處理
  • 前端: CoffeeScript, backbone.js, jQuery, sass
  • lucene solr 作為搜尋之用

Email – 從 Gmail 到 Sendgrid

由於直接用 EC2 instance 送信很容易被判定為 spam,所以剛開始 iDoneThis 是用 gmail 的 BCC 來送信的,之後就換成了 SendGrid

iDoneThis 寫了一個 script “sendmail” 來跟 SendGrid API 互動,直到今天這個 script 還是被使用著,不過可靠度方面也大大改善了。剛開始的 sendmail 只是一個簡單的迴圈來處理資料庫當中的東西,現在已經變成了會追蹤寄送 e-mail 當中的不同狀態並且做些相對應的處理。

為了處理收到的 e-mail,剛開始 iDoneThis 寫了一個大概兩百行的 “getmail” script 來透過 IMAP 存取 gmail,起初這個 script 很不穩定,而且當 script 出錯的時候會讓資料庫爛掉,所以在跑 getmail 必須有人在旁邊看著。不只這樣,還必須來處理一些不必要的文字(簽名檔、回文等等)以及 encoding 等等的問題,為了要搞定這件事情,getmail 長到了大約 800 行左右。

持續了一段時間之後,iDonThis 捨棄了 getmail,轉為使用 SendGrid 的 incoming parse API。主要的原因是因為 gmail 對 IMAP 存取有 rate limit,所以沒辦法快速的把 email 處理完。

規模化

起初,iDoneThis 主要面對到的瓶頸不是在技術上面,而是在開發時間上面,就算到今天還是如此,對 iDoneThis 來說,這代表著效能以及規模化的議題主要是圍繞在簡化程式設計上面。

想要做到這件事情,第一步就是盡可能的限制要開發的功能,或是把一些部分外包出去。只有在確定開發出來的功能有人用之後才會做優化。

再來,儘管可能會犧牲效能,還是要盡可能讓程式碼精簡。舉例來說,使用 Django ORM 不一定會產生有效率的 SQL 查詢,但是只有在實際上線而且發現會產生效能問題之後才會做這部份的優化。這麼做除了可以避免過度的優化外,也讓程式碼看起來乾淨許多。

隨著收到和寄出的信件越來越多,iDonThis 考慮到轉換到多台 server 的架構。不過這會讓架構上面複雜不少,也會帶來些許的麻煩。為了可以節省開發者的時間和簡化成是架構,儘管可以用好幾台 small EC2 instance 搞定一樣的事情,iDonThis 還是用一台 xlarge EC2 instance 搞定。

最重要的部份是 iDonThis 有做到 continuous deployment,每個開發者都可以在幾秒之內把程式碼 deploy 到 production server,這代表著開發環境跟 production 會是一致的,這也代表在某個功能上線之後可以很快的做出相對應的反應。

重新架構

剛開始的 iDoneThis 只是一個很簡單的 Django 專案,只有幾個 models、views 和 templates。儘管產品一直在進步,不過 iDoneThis 依舊是在比較舊的 web 應用架構上面 – 所有在上面發生的事情都伴隨著頁面讀取和送出表單。有一段時間這樣運作的還不賴,不過過了一段時間之後使用者希望可以有更好的使用者介面來存取他們的資料。

起初 iDoneThis 用了一堆 jQuery 來完成需要做的工作,雖然可以動,但是 code 變得很混亂而且很難除錯。為了希望盡可能的簡化前端的程式碼,原本的 jQuery code 被 CoffeeScript 和 Backbone.js 取代。隨著時間的演進,iDoneThis 的 backend 變成了 API-based 的架構。Django web service 分為兩個部分,一部分是負責提供 json API,另外一部分是負責吐出前端的程式碼以及 html 的部份。

目前為止,這種模式的程式碼比舊方法簡單許多而且更容易除錯,儘管還是花了不少時間才變成如今的樣子,不過由於程式碼簡潔許多,所以之後也可以更容易的增加新功能以及除錯。

這當中 iDoneThis 學到了什麼

  • 把麻煩的事情外包,而且是盡可能的外包。iDoneThis 來說,把所有 email 相關的事情都交給 SendGrid 處理。當然可能在某些時間點或許把這些事情自己刻些 tool 來處理是有意義的,不過對於新創公司來說,這個時間點可能會是在遙遠的未來。
  • 簡化比效能重要。儘管或許可以用 nginx 跑在一個比較小的 EC2 instance 上面,不過用 apache mod_wsgi 一切都處理的很好。對 iDoneThis 來說,都是盡可能的用最簡單的方法來完成工作,關於效能的問題之後再考慮。
  • 有一小段時間 iDoneThis 打算把資料庫和 web server 分開跑在不同的機器上面,不過後來發現帶來的問題大過於好處,所以現在都是跑在同一台 EC2 instance 上面。

參考文章: IDoneThis – Scaling An Email-Based App From Scratch



Comments
  1. 回覆

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *