Compilify - 讓你在瀏覽器中編譯.NET代碼
作者 Roopesh Shenoy 譯者高翌翔
Compilify 是一款以服務形式出現的在線編譯器,其創始人是 Justin Rusbatch,它運行在 Roslyn CTP[1]之上。從最近開始,它已得到來自 .NET 社區的大量關注。我們與 Justin 取得了聯系,并向他請教了幾個問題。
InfoQ:請向我們的讀者做下自我介紹吧?
Justin: 我叫 Justin,是名自學開發者,現在就職于一家基于 .NET 進行網絡開發的小型公司,公司位于賓夕法尼亞州的中部。當我還是計算機操作員時,我利用值夜班時為大型機安裝磁帶的間隙自學了C#。我使用 ASP.NET 工作了一年,不過 MVC 框架一經問世我就轉移到了它上面,而且從那時起我已用 MVC 框架開發了好幾個網站。我也喜歡學習其他語言,對 Ruby on Rails、node.js、以及F#都略懂一二。
InfoQ:你最近啟動了 Compilify 項目——請解釋一下其用途何在?
Justin: Compilify(讀作“compile-ify”)的靈感源于好幾件事兒。靈感的主要來源之一就是 Roslyn CTP 給 Visual Studio 帶來的 C# 交互窗口(C# Interactive window)。此窗口提供了一種替代環境,以便在開發中的項目上來執行個別語句,并直接得到結果。
Compilify 使 .NET 編譯器成為完全便攜式的,而且可通過快捷的接口訪問,從而促進共享和協作。它不是位于瀏覽器中的集成開發環境(IDE),而且永遠也不會那樣。其實它比那簡單得多。為了試驗只需幾行代碼的新點子,你無需啟動集成開發環境(IDE)或新建控制臺項目。開發者的時間非常寶貴。正如子曰“思而不學則殆”,花太多時間思考問題卻未曾實際嘗試過任何解決方案,這樣只會導致設計過度的解決方案、并扼殺生產力。
Compilify 作為幫助新手學習 C# 的教學工具同樣具有很大潛力。下載、安裝、啟動 Visual Studio 可能令新手望而卻步。實際上,某些開發者無法安裝 Visual Studio 可能是由于安裝了其他不能與之并行的應用程序——從而導致了更加不堪回首的經歷。Compilify 使得在無需安裝任何程序、甚至連瀏覽器插件都不需要的情況下,用戶即可上手學習C#。
InfoQ:請解釋一下 Compilify 的幕后運行原理?
Justin: 其結構非常巧妙!
一旦用戶向服務器提交代碼執行,就會用 SignalR 來建立持久連接。Web 服務器使用 SignalR 連接 ID 將接收到的代碼打包成對象,然后將其添加到位于我的 Redis 服務器上的處理隊列中。從而釋放 Web 服務器,以便繼續處理來自其他用戶的請求。
盡管處理過程說起來很簡單,但是后臺工作服務器的處理任務卻十分繁重。為了防止運行任何惡意代碼,每次執行代碼時都會新建充當安全沙箱角色的、低信任級別的應用程序域(AppDomain)。雖然我沒有花時間分析過性能,不過到目前為止,在該應用程序的現階段下我還無需擔心性能問題。因為在應用程序域(AppDomain)中,除了用戶代碼之外,只加載了一些必要的程序集。
用戶代碼先被包裝成方法、然后解析成編譯單元、進而釋放到程序集中。在沙箱內部會加載該程序集,并調用用戶代碼所包裝成的方法。執行結果被序列化,并返回給工作服務器。我在單獨的線程中執行這些工作,以便萬一耗時太久(目前設置的時間限制是 5 秒)我可以取消處理。
一旦執行結果返回給了工作服務器,工作服務器就會使用相應的 SignalR 連接 ID(此 ID 是為了執行此代碼,在最初創建請求時得到的)、通過 pub/sub(即 publish/subscribe,發布/訂閱)通道將執行結果發布回 Redis 服務器。Web 服務器會在 App_Start 上訂閱此通道。然后以便 SignalR 通過此通道將任何消息轉發給相應的客戶端。
為了便于安全執行用戶代碼,并確保 Web 服務器的穩定性,因此這種復雜架構是有必要的。
InfoQ:當鍵入代碼時,編輯器幾乎會在瞬間做出響應,盡管如此,這仍需一次到服務器的往返行程,你是如何做到的呢?
Justin: 驗證用戶代碼的過程會在輸入完成后0.5秒開始。編輯器里的內容會使用標準的 AJAX 請求以 POST 方式發送至服務器。在服務器上,會使用 Roslyn 對代碼進行解析、并檢查各種語法或引用錯誤。然而一旦出現錯誤,實際上就會終止將編譯單元釋放到程序集的處理。任何錯誤都會返回給客戶端,并顯示給用戶。
InfoQ:粗算一下,你花了多少時間或精力來構建此項目?
Justin: 在推出 Compilify 之前,我在此項目上工作了一周半的時間。當然離完成還差得很遠。實際上,我在 04 月 11 日發布的版本確實只是個概念驗證。我希望通過發布此版本可以收到一些反饋,而且最好能引起大家的一些興趣——不過讓我始料未及的是,收到的反饋幾乎和流量一樣多。
InfoQ:一般來說,你收到的流量是何種狀況,而且你為了滿足流量要求,平均需要多少臺前端 Web 服務器及后臺工作服務器?
Justin: 自從 04 月 11 日推出以來的一周時間里,網站點擊量已接近 20,000。用戶已保存、驗證或執行代碼超過 70,000次。大多數流量發生在上周 推ter 發現此站點之后。在 John Galloway 發微博評論此站點以后產生了不錯的負載量——并發會話數保持在 50 至 60 之間。隨即 Scott Hanselman 也對此發微博評論,在之后不到五分鐘的時間里,該值就增加了兩倍,并發會話數的峰值接近 170。因此我必須趕緊調整至 3 臺 Web 服務器和 2 臺后臺工作服務器,以便跟上負載的增長。
如前所述,我確實對這種流量始料未及,而且要是沒有來自 AppHarbor 那幫哥們的幫助,我可能無法解決這種狀況。和他們聯系很容易,而且他們給我的信息有助于識別那些我能做的改變,以便降低負載。
通過在 Web 應用程序(負責處理代碼驗證)與后臺工作服務器(負責編譯并執行代碼)之間保持 Redis 隊列,我能夠很容易地擴展該應用。要是隊列開始變長,我就增加更多的后臺工作服務器,要是前端開始變得不堪重負,我就增加更多的 Web 服務器。用于 AppHarbor 的附加組件 New Relic 確實可以讓我輕松監視 Web 服務器和后臺工作服務器的負載。
InfoQ:對于 Rosyln、SignalR、Redis、或是在該項目中用到的其他組件,你有哪些特別的學習經驗可以分享?
Justin: 盡管 SignalR 是一款功能強大的工具,并且極其容易建立,但是需要注意你的使用方式。由于它也很快,因此讓人感覺有些輕量級。我就曾犯過在頁面加載時打開連接卻永遠不關閉的錯誤。而對于像 http://jabbr.net(由 David Fowler 創建的聊天應用)等應用,這種行為卻是必要的。
不過在我的情況下,就不必那樣做。直到用戶為了運行代碼點擊鏈接以前,我都無需推送消息到客戶端。而且一旦執行結果被推送到客戶端,也就無需保持連接的開啟狀態。自從我按需開啟連接之后,服務的負載便隨之急劇下降。盡管已有許多關于 SignalR 的示例,但它們大多是在展示某種用法,例如 Jabbr,因此它們并不會教你如何關閉連接。
Compilify 是位于 github 上的開源項目。該服務位于 AppHarbor 云平臺之上,AppHarbor 公司最近資助了該項目。他們在其博客上也推出了一篇對 Justin 的采訪,其中包含更多細節。
譯注
[1] Roslyn CTP,傳統上,編譯器都是黑盒——源代碼從一端進入,然后對象文件或程序集從另一端出來。Roslyn 項目通過開放 VB 和 C# 編譯器改變了這種模型。編譯器提供了各種 API(應用程序編程接口),從而使得工具和最終用戶可以共享編譯器所擁有的與代碼有關的豐富信息。通過微軟的“Roslyn”CTP(即 Community Technology Preview,社區技術預覽版)版可預覽新的語言對象模型,該模型用于代碼生成、分析和重構,還有即將到來的腳本支持、及 C# 與 VB 的交互使用。更多內容請參閱 Microsoft “Roslyn” CTP 下載頁面。
查看英文原文:Compilify – Compile .NET Code In A Browser