Go 部落格

使用 Go 構建 StatHat

Patrick Crosby
2011年12月19日

引言

我的名字是 Patrick Crosby,我是 Numerotron 公司(StatHat 的開發者)的創始人。我們最近釋出了 StatHat。這篇博文將介紹我們選擇使用 Go 來開發 StatHat 的原因,包括我們如何使用 Go 的詳細資訊。

StatHat 是一個用於跟蹤程式碼中統計資料和事件的工具。從 HTML 設計師到後端工程師,任何人都易於使用 StatHat,因為它支援從 HTML、JavaScript、Go 和其他十二種語言傳送統計資料。

您將數字傳送到 StatHat;它會生成精美的、可完全嵌入式圖表來展示您的資料。當發生指定的觸發器時,StatHat 會提醒您,向您傳送每日電子郵件報告,以及更多功能。因此,您無需花費時間編寫應用程式的跟蹤或報告工具,而是可以專注於程式碼。當您進行實際工作時,StatHat 會保持高度警惕,就像雄鷹在其山頂巢穴中一樣,或者像服用興奮劑的保姆。

這是一個 StatHat 圖表,顯示了紐約、芝加哥和舊金山的氣溫

架構概述

StatHat 由兩個主要服務組成:接收統計/事件 API 呼叫以及用於檢視和分析統計資料的 Web 應用程式。我們希望儘可能將它們分開,以將資料收集與資料互動隔離開。我們這樣做的原因有很多,但一個主要原因是,我們預計會處理大量的自動化傳入 API HTTP 請求,因此 API 服務將採用不同於與人類互動的 Web 應用程式的最佳化策略。

Web 應用程式服務是多層級的。Web 伺服器處理所有請求並將其傳送到互動層。對於簡單任務,互動器將負責生成任何必要的資料。對於複雜任務,互動器依賴多個應用程式伺服器來處理生成圖表或分析資料集等任務。互動器完成後,Web 伺服器會將結果傳送給呈現器。呈現器以 HTML 或 JSON 形式響應 HTTP 請求。隨著服務需求的增長和變化,我們可以水平擴充套件 Web、API、應用程式伺服器和資料庫。不存在單點故障,因為每個應用程式伺服器都有多個副本在執行。互動層允許我們擁有系統的不同介面:http、命令列、自動化測試、移動 API。StatHat 使用 MySQL 進行資料儲存。

選擇 Go

我們在設計 StatHat 時,對我們的開發工具有一個以下的清單

  • 後端和前端系統使用相同的程式語言

  • 良好的、快速的 HTML 模板系統

  • 快速啟動、重新編譯、測試,方便大量試錯

  • 單臺機器處理大量連線

  • 用於處理應用程式級併發的語言工具

  • 良好的效能

  • 強大的 RPC 層,用於在不同層級之間通訊

  • 大量的庫

  • 開源

我們評估了許多流行和不那麼流行的 Web 技術,最終選擇了 Go 來開發它。

當 Go 於 2009 年 11 月釋出時,我立即安裝了它,並對其快速的編譯時間、goroutines、channels、垃圾回收以及所有可用的包非常滿意。我尤其對其應用程式使用的程式碼行數之少感到高興。我很快嘗試製作了一個名為 Langalot 的 Web 應用,該應用可以在您鍵入查詢時併發搜尋五個外語詞典。它速度極快。我將其上線,自 2010 年 2 月以來一直在執行。

接下來的部分將詳細介紹 Go 如何滿足 StatHat 的要求,以及我們使用 Go 解決問題的經驗。

執行時

我們使用標準的 Go http 包 來處理我們的 API 和 Web 應用伺服器。所有請求首先透過 Nginx,任何非檔案請求都會被代理到 Go 驅動的 http 伺服器。後端伺服器都用 Go 編寫,並使用 rpc 包 與前端通訊。

模板

我們使用標準的 template 包 構建了一個模板系統。我們的系統增加了佈局、一些通用格式化函式以及在開發期間動態重新編譯模板的能力。我們對 Go 模板的效能和功能非常滿意。

試錯

在我以前的一份工作中,我曾參與開發一款名為《Throne of Darkness》的影片遊戲,該遊戲是用 C++ 編寫的。我們有一些標頭檔案,一旦修改,就需要對整個系統進行完全重建,耗時 20-30 分鐘。如果有人修改了 Character.h,他就會遭受其他所有程式設計師的怒火。除了這種痛苦之外,它還顯著減慢了開發速度。

從那時起,我一直試圖選擇允許快速、頻繁試錯的技術。使用 Go,編譯時間不成問題。我們可以在幾秒鐘內重新編譯整個系統,而不是幾分鐘。開發 Web 伺服器即時啟動,測試在幾秒鐘內完成。如前所述,模板在更改時會重新編譯。結果是 StatHat 系統非常易於使用,並且編譯器不是瓶頸。

RPC

由於 StatHat 是一個多層系統,我們想要一個 RPC 層,以便所有通訊都是標準的。使用 Go,我們使用 rpc 包gob 包 對 Go 物件進行編碼。在 Go 中,RPC 伺服器只需獲取任何 Go 物件並註冊其匯出的方法。無需中間介面描述語言。我們發現它非常易於使用,並且我們的許多核心應用程式伺服器程式碼行數少於 300 行。

我們不想花時間重寫 SSL、資料庫驅動程式、JSON/XML 解析器等庫。儘管 Go 是一門年輕的語言,但它有許多系統包和越來越多的使用者貢獻的包。除了少數例外,我們找到了 Go 包來滿足我們所需的一切。

開源

根據我們的經驗,使用開源工具非常有價值。如果出現問題,能夠檢查每個層的原始碼並且沒有任何黑盒子是極其有益的。擁有語言、Web 伺服器、包和工具的程式碼使我們能夠理解系統的每個部分是如何工作的。Go 中的一切都是開源的。在 Go 程式碼庫中,我們經常閱讀測試,因為它們通常提供瞭如何使用包和語言特性的絕佳示例。

效能

人們依賴 StatHat 來即時分析他們的資料,我們需要系統儘可能響應迅速。在我們的測試中,Go 的效能遠遠優於大多數競爭對手。我們將其與 Rails、Sinatra、OpenResty 和 Node 進行了比較。StatHat 一直透過跟蹤請求的各種效能指標、某些任務的持續時間、使用的記憶體量來監控自身。因此,我們能夠輕鬆評估不同的技術。我們也利用了 Go 測試包的基準效能測試功能。

應用程式級併發

在我過去的職業生涯中,我是 OkCupid 的 CTO。我在那裡使用 OKWS 的經驗讓我認識到非同步程式設計的重要性,尤其是在動態 Web 應用程式方面。您永遠不應該同步執行類似以下的操作:從資料庫載入使用者,然後查詢他們的統計資料,然後查詢他們的警報。這些都應該併發執行,但令人驚訝的是,許多流行的框架沒有非同步支援。Go 在語言級別支援這一點,而不會出現任何回撥地獄。StatHat 大量使用 goroutines 來併發執行多個函式,並使用 channels 在 goroutines 之間共享資料。

託管和部署

StatHat 執行在 Amazon 的 EC2 伺服器上。我們的伺服器分為幾種型別

  • API

  • Web

  • 應用程式伺服器

  • 資料庫

每種型別的伺服器至少有兩個,並且它們位於不同的區域,以實現高可用性。新增一臺新伺服器只需幾分鐘。

為了部署,我們首先將整個系統構建到一個帶時間戳的目錄中。我們的打包指令碼構建 Go 應用程式,壓縮 CSS 和 JS 檔案,並複製所有指令碼和配置檔案。然後,該目錄分發到所有伺服器,使它們擁有完全相同的發行版。每臺伺服器上的一個指令碼會查詢其 EC2 標籤,確定它負責執行什麼,並啟動/停止/重啟任何服務。我們經常只部署到一部分伺服器。

更多

有關 StatHat 的更多資訊,請訪問 stathat.com。我們正在釋出一些我們編寫的 Go 程式碼。前往 www.stathat.com/src 獲取所有開源的 StatHat 專案。

要了解更多關於 Go 的資訊,請訪問 golang.org

下一篇文章: 瞭解 Go 社群
上一篇文章: 從零到 Go:24 小時內在 Google 主頁上線
部落格索引