移動后端支持平臺Parse將API由Ruby遷移到Go
Charity Majors是移動后端支持平臺Parse的工程師。近日,他 撰文 介紹了他們將API從Ruby遷移到Go的過程。
2011年, Parse 借助Ruby on Rails快速推出了第一個版本。他們用 Unicorn 作為HTTP服務器,用 Capistrano 部署代碼,用 RVM 管理環境,并用許多開源gems處理 YAML 解析、oAuth、JSON解析、MongoDB及MySQL等方面的工作。此外,他們還用Chef管理他們的基礎設施。
起初,平臺運行良好。但隨著代碼庫的增長,部署時間越來越長,Unicorn服務器無法很平穩地重啟。到2012年底,他們有200個API服務器運行在 m1.xlarge 類型的實例上,每個實例24個Unicorn工作進程。該平臺每秒需要為來自60000個移動應用的3000個請求提供服務。一次完整部署或回滾需要20 分鐘。另外,在通常的Ruby on Rails設置中,工作進程池大小固定,每個工作進程一次只能處理一個請求。隨著API流量和應用數量的增加,“一個請求一個進程”的Rails模型開始 無法滿足平臺的擴展需求,而增加數據庫和工作進程也增加了故障點和性能退化的可能。
于是,他們認為,他們需要遷移到一個完全不同于Rails的異步工作模型。他們對比了如下選項:
- EventMachine :絕大多數Ruby gems都不是異步的,而且許多不是線程安全的,所以經常很難找到一個庫可以異步地處理一些常見任務。
- JRuby :JRuby從根本上說還是Ruby,同樣缺少異步庫支持。而對于Java,他們的后端或Ops團隊不愿意部署和調優JVM。
- C++ :他們現有的C++代碼已經難以調試和維護。與其它現代編程語言相比,C++開發的效率不是很高。C++也缺少一些他們需要的庫,比如HTTP請求處理。C++可以處理異步操作,但并不好用。
- C# :C#有最好的并發模型,并且支持Async和Await。但真正的問題在于,Linux對C#開發的支持不夠好,缺少一些與常用開源工具互操作的庫。如果選擇C#,那么他們就需要對工具鏈做重大調整。
- Go :同C#一樣,Go也在底層內置了異步操作。MongoDB Go驅動程序可能是現有最好的MongoDB驅動程序,而與MongoDB的復雜交互是Parse的核心所在。Goroutines比線程更輕量級。
最終,他們選擇了Go,用它重寫了EventMachine推送后端、核心API服務器,實現了Rails中間件的處理功能,結果:Parse平 臺的可靠性提高了一個數量級;API不再因為數據庫和后端服務的增加而日益變得脆弱;由于去掉了大量的gems和隱含假設,代碼庫變得更干凈;Ops團隊 不用每周處理多次Ruby API故障了;預分配的API服務器池縮減了約90%;移除了孤立的Rails API服務器筒倉,簡化了架構;完整集成測試的時間由25分鐘縮減至2分鐘;完整API服務器部署(包含滾動重啟)的時間由30分鐘縮減至3分鐘;Go API服務器啟動更平穩。
雖然Parse成功地實現了遷移,但是許多網友都認為Majors給出的選擇理由并不充分。網友pron指出,JVM易于監控、控制、調試和性能 分析。在大多數情況下,JVM調優只需要設置GC方式、最大堆大小和新生代比率三個值。對此,Majors的答復是,“我們討厭有關Java和JVM的一 切”。網友headius則為Parse團隊沒有試用JRuby感到可惜,因為單個JRuby實例就可以處理成百上千的并發請求,而且不需要采用異步方 式。他認為,JRuby團隊不需要重寫就可以解決Parse的問題。網友Zazi Bazuka則這樣評論道:
我討厭這篇博文,攻擊Ruby成了現在的一種趨勢。但攻擊總是來自不了解Ruby生態系統的人。為什么不使用 Puma 或其它多線程服務器?!為什么使用EventMachine?互聯網上到處都是不應該使用它的文章,你們應該使用 celluloid.io 代替它。
Majors的文章 在Hacker News上也引發了激烈的討論 ,其中許多網友同樣對Parse的選擇過程存在疑問。網友aikah就表示:
當了解業務域和性能問題時,Go是一個不錯的遷移目標。但是,我不推薦初學者用它構建安全的Web應用程序,因為……Go確實不適合用于傳統 Web站點的快速開發。讓我吃驚的是,Parse用Rails編碼,我原以為他們以Node.js為基礎構建,因為Cloud Code用了JS。
對此,lacker答復說:
我們考慮過它。那時,一個很大的Node代碼庫看上去很難維護。不過,工具比那時好了。如果我們現在重新討論,我認為,采用Node的理由會更令人信服,如果我們等到ES6成為標準后再討論,理由還會更充分。