這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
前幾天我寫了一篇文章: 超全的Go Http路由架構效能比較,利用Julien Schmidt實現的benchmark測試架構對幾乎所有的go web架構的路由功能進行了比較。我本來以為對Go web架構的效能考察就告以段落了,直到我寫了一段簡單的代碼測試Irsi,用來類比實際產品中的處理,才發現了Julien Schmidt測試架構的問題。
這段代碼是這樣的:
12345678910111213141516171819202122 |
package mainimport ("fmt""os""strconv""time" "github.com/kataras/iris")func main() {api := iris.New()api.Get("/rest/hello", func(c *iris.Context) {sleepTime, _ := strconv.Atoi(os.Args[1])if sleepTime > 0 {time.Sleep(time.Duration(sleepTime) * time.Millisecond)}c.Text("Hello world")})api.Listen(":8080")} |
當我將實際業務的處理時間類比為10毫秒的時候,使用100並發進行測試:
wrk -t16 -c100 -d30s http://127.0.0.1:8080/rest/hello,Iris吞吐率才達到97 requests/second。詳細介紹可以看我的文章: iris 真的是最快的Golang 路由架構嗎? 。
雖然Iris的作者很快做了修改,臨時解決了這個問題,但是也促使我重新審視Julien Schmidt測試架構,也促使我實現了一個測試Go web framework benchmak的架構: Go web framework benchmark。
2016/04/12 updated: 現在Iris已經改成了fasthttp實現,效能超級好。
聲明
最近一些人指控Iris的作者"偷"了其他人的代碼,比如httprouter,而沒有聲明著作權。基本上Iris的高效能來源於fasthttp和httprouter等一些架構的努力,作者在github上對iris項目的描述"世界上最快的web架構"明顯是拉仇恨的。
雖然我對整個事件比較明了,但是我無意於加入這場爭論,我建立go web framework benchmark的目的就是為了協助開發人員評估各個架構的效能,而不是要比個高下,更不期望以我的項目的測試結果去攻擊別人或者貶低別人。
基本上go web架構分為兩個門派,基於標準庫net/http的架構和基於fasthttp庫的架構。明顯,基於fasthttp庫的架構效能要好於標準庫,但是它們也有不足之處,比如和標準庫不相容(或者不容易相容),不支援http2等,你在選擇的時候要懂得取捨。
重新審視Julien Schmidt測試架構
如果查看Julien Schmidt測試架構的測試結果以及實現,可以看到他測試的只是web架構的路由功能,包括路徑中參數的解析, 並不是測試一個完整的web架構的處理(接受串連、路由、Handler處理)。
他利用Go的benchmark測試架構的方法,實現了N多的Benchmark方法,通過手工建立一個http.Request,傳遞給router進行路由處理。
並且,他的Handler的業務非常的簡單,有的handler裡面沒有任何商務邏輯,只是空的方法體,有的只是將參數寫回到Response中。這並不能反映實際的產品的業務處理。
在實際的業務中,Handler必然要包含一定的業務處理,也出處理的時間可能從幾毫秒到上百毫秒不等,一些商務邏輯處理起來還非常的慢,比如:
- 從一個網路連接中讀取資料
- 寫資料到硬碟中
- 訪問資料庫
- 訪問快取服務器
- 調用其它服務,等待服務結果的返回
- ……
如果加上這些商務邏輯的處理時間,各個web架構benchmark是否還如路由功能的benchmark一樣呢?
因為路由的處理時間在整個web架構中的處理時間所佔的比例比較小,所以實際的web架構的benchmark有可能和路由的benchmark關係不大。即使路由的效能最好,如果handler的處理沒有很好的處理的化,也可能整體的效能也不會特別出色。
如果想實現一個高效能的路由器,一定要盡量避免在路由處理時分配新的對象,高效能的路由器都實現了零分配,如httprouter、Iris等。
很多web架構的Handler處理和Go的預設實現一樣,還是在串連所在的goroutine中執行,也有的架構實現了goroutine池,由一個goroutine池來處理串連和handler,避免太多的goroutine分配和回收,效能可能會好一些。
實現一個Go web framework benchmark庫
所以,有必要實現一個新的測試架構(庫),完整測試web架構的效能,包括串連、路由、handler處理等,基於此,我實現了Go web framework benchmark。
它具有下面的功能:
- 為每個web架構實現了/hello的Http Get服務,它返回
hello world字串。所有的web架構的實現都是一致的。
- 可以指定業務處理的時間,如10毫秒,100毫秒,500毫秒等
- 自動化測試
這個架構並沒有測試其它功能,如Post、Put,也沒有測試路由的參數解析的功能,它的本意是期望通過Get方法的測試來比較各個架構對業務的處理的能力。
根據Julien Schmidt測試架構中測試到的go web架構,在加上lion,fasthttp,一共測試了下面的web架構。
- default http
- macaron
- go-json-rest
- beego
- pat
- fasthttprouter
- lion
- httptreemux
- baa
- go-restful
- gin
- martini
- lars
- bone
- gocraft
- gorilla
- httprouter
- iris
- tango
- vulcan
- possum
- denco
- traffic
- ace
- fasthttp-routing
- go-tigertonic
- fasthttp
- r2router
- goji
- gojiv2
- echo
基本測試
首先我們先看一些商務邏輯處理時間分別為0ms,10ms,100ms,500ms時的各web架構的表現。
測試的並發數為5000。
1 |
wrk -t16 -c5000 -d30s http://127.0.0.1:8080/hello |
平均處理時間(Latency)
記憶體佔用
註解:
- possum在測試的時候出現N多的
http: multiple response.WriteHeader calls錯誤,它的效能比較低下。以下的測試possum也有問題。
- 預設的go標準庫的實現效能已經很高
- 處理時間10ms和100ms, 各架構的效能差別不是特別巨大,但是500ms吞吐率下降的很厲害。
- 基於fasthttp實現的路由器/web架構表現非常好,如iris, fasthttprouter、fasthttp-routing, echo-fasthttp,但是echo-fasthttp由於要與net/http相容,效能稍差一點。
如果開啟http pipelining, fasthttp會遠遠好於net/http實現的架構。
並發量測試
我們以商務邏輯處理時間為30ms為基準,測試並發量為100,1000,5000的情況下web架構的效能。
註解:
- 大部分的架構的效能差不太多
- fasthttp表現還是非常好,是效能最好的架構,尤其在並發量大的情況下
- 並發量在1000的時候大部分的架構的吞吐率能達到3萬/秒,並發量在5000的時候大部分二代架構能達到4萬/秒
如果開啟http pipelining, fasthttp會遠遠好於net/http實現的架構。
綜上測試,大部分的go web架構效能表現還可以,加上處理時間的測試後效能差別不是很大,不像測試路由功能一樣涇渭分明。
fasthttp表現非常的好,需要考慮的是如果選它做web架構,你的代碼將難以遷移到別的架構上,因為它實現了和標準庫net/http不一樣的介面。
2016/04/19 更新: 增加了延遲時間Latency和記憶體佔用的測試資料。