go語言web架構比較:gin vs iris vs echo

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

前言

由於golang提供了完善的net/http標準庫,基於該標準庫實現一個web架構的難度相比其他語言低了不少,所以go web架構簡直就是百花齊放。從老牌的revel和beego,到新出的gin,和iris等,而且還有一些類似於chi這種router。個人一般小項目,尤其是中介軟體需要暴露一些http介面的,基本就使用chi即可。
本次測試主要是gin iris echo 這三個架構。側重在於高效能,從並發和json序列化和還原序列化兩個方面來測評,畢竟後台項目側重的也就是這兩個方面。

測試

測試環境說明

為了選擇符合重IO的架構,現設定如下情境的demo,demo的具體要求如下:

  1. 開啟日誌功能(類比正常業務時也會記錄日誌),在請求開始和結束時分別記錄一條日誌
  2. 介面中用sleep暫停1秒,假設這裡的網路IO操作(同時更容易從日誌看出是否協程並發的行為)
  3. 用POST介面做測試,介面中不進行任何處理,把接收到的body直接序列化返回(序列化和還原序列化是架構最高頻的動作)
  4. 開啟架構的accesslog功能

測試載入器以及情境如下

  1. 測試載入器使用經典的jmeter,直接使用GUI介面測試
  2. 情境分為10線程並發,100線程並發,500線程並發,1000線程並發和1500線程並發
  3. 所有結果都只看jmeter的彙總報告,重點查看輸送量、時間和錯誤數
  4. 所有demo啟動的時候均啟動單線程,非同步架構不限制協程的數量,設定GOMAXPROCS=1
  5. 所有測試均在本地,壓測時間長度兩分鐘
  6. 測試時採用POST請求,資料樣本有565bytes、5KB、10KB、50KB和100KB,每個樣本都要在不同並發線程上測試

測試代碼

gin:

package mainimport (    "log"    "net/http"    "time"    "github.com/gin-gonic/gin")// Agent ...type Agent struct {    AgentID  string `json:"agent_id"`    QueuedAt string `json:"queued_at"`    QueuedBy string `json:"queued_by"`}// Details ...type Details struct {    EventID  string `json:"event_id"`    Endpoint string    Metric   string    Content  string    Priority int    Status   string}// Test1 ...type Test1 struct {    Agent       Agent    Details     Details    Description string    EventType   string `json:"event_type"`    ServiceKey  string `json:"service_key"`}// Test2 test2type Test2 struct {    Data []*Test1}func main() {    r := gin.New()    // Global middleware    // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.    // By default gin.DefaultWriter = os.Stdout    r.Use(gin.Logger())    r.GET("/ping", func(c *gin.Context) {        c.JSON(200, gin.H{            "message": "pong",        })    })    r.POST("/v1/test", func(c *gin.Context) {        var test Test1        if err := c.BindJSON(&test); err == nil {            log.Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            log.Println("=========================end io=======================")            c.JSON(http.StatusOK, test)        } else {            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})        }    })    r.POST("/v2/test", func(c *gin.Context) {        var test Test2        if err := c.BindJSON(&test); err == nil {            log.Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            log.Println("=========================end io=======================")            c.JSON(http.StatusOK, test)        } else {            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})        }    })    r.POST("/v3/test", func(c *gin.Context) {        var test Test2        if err := c.BindJSON(&test); err == nil {            log.Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            log.Println("=========================end io=======================")            c.JSON(http.StatusOK, test)        } else {            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})        }    })    r.POST("/v4/test", func(c *gin.Context) {        var test Test2        if err := c.BindJSON(&test); err == nil {            log.Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            log.Println("=========================end io=======================")            c.JSON(http.StatusOK, test)        } else {            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})        }    })    r.Run() // listen and serve on 0.0.0.0:8080}

iris:

package mainimport (    "time"    "github.com/kataras/iris"    "github.com/kataras/iris/middleware/logger")// Agent ...type Agent struct {    AgentID  string `json:"agent_id"`    QueuedAt string `json:"queued_at"`    QueuedBy string `json:"queued_by"`}// Details ...type Details struct {    EventID  string `json:"event_id"`    Endpoint string    Metric   string    Content  string    Priority int    Status   string}// Test1 ...type Test1 struct {    Agent       Agent    Details     Details    Description string    EventType   string `json:"event_type"`    ServiceKey  string `json:"service_key"`}// Test2 test2type Test2 struct {    Data []*Test1}func main() {    app := iris.New()    app.Use(logger.New())    app.Get("/ping", func(c iris.Context) {        c.WriteString("pong")    })    app.Post("/v1/test", func(c iris.Context) {        var test Test1        if err := c.ReadJSON(&test); err == nil {            app.Logger().Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            app.Logger().Println("=========================end io=======================")            c.JSON(test)        } else {            c.WriteString("failure")        }    })    app.Post("/v2/test", func(c iris.Context) {        var test Test2        if err := c.ReadJSON(&test); err == nil {            app.Logger().Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            app.Logger().Println("=========================end io=======================")            c.JSON(test)        } else {            c.WriteString("failure")        }    })    app.Post("/v3/test", func(c iris.Context) {        var test Test2        if err := c.ReadJSON(&test); err == nil {            app.Logger().Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            app.Logger().Println("=========================end io=======================")            c.JSON(test)        } else {            c.WriteString("failure")        }    })    app.Post("/v4/test", func(c iris.Context) {        var test Test2        if err := c.ReadJSON(&test); err == nil {            app.Logger().Println("========================start io=====================")            time.Sleep(time.Duration(1) * time.Second)            app.Logger().Println("=========================end io=======================")            c.JSON(test)        } else {            c.WriteString("failure")        }    })    // Start the server using a network address.    app.Run(        iris.Addr(":8080"),        // disables updates:        iris.WithoutVersionChecker,        // skip err server closed when CTRL/CMD+C pressed:        iris.WithoutServerError(iris.ErrServerClosed),        // enables faster json serialization and more:        iris.WithOptimizations)}

echo:

package mainimport (    "log"    "net/http"    "time"    "github.com/labstack/echo"    "github.com/labstack/echo/middleware")// Agent ...type Agent struct {    AgentID  string `json:"agent_id"`    QueuedAt string `json:"queued_at"`    QueuedBy string `json:"queued_by"`}// Details ...type Details struct {    EventID  string `json:"event_id"`    Endpoint string    Metric   string    Content  string    Priority int    Status   string}// Test1 ...type Test1 struct {    Agent       Agent    Details     Details    Description string    EventType   string `json:"event_type"`    ServiceKey  string `json:"service_key"`}// Test2 test2type Test2 struct {    Data []*Test1}func main() {    // Echo instance    app := echo.New()    // Middleware    app.Use(middleware.Logger())    // Routes    app.GET("/ping", func(c echo.Context) error {        return c.String(200, "pong")    })    app.POST("/v1/test", func(c echo.Context) error {        var test Test1        if err := c.Bind(&test); err != nil {            return err        }        log.Println("========================start io=====================")        time.Sleep(time.Duration(1) * time.Second)        log.Println("=========================end io=======================")        return c.JSON(http.StatusOK, test)    })    app.POST("/v2/test", func(c echo.Context) error {        var test Test2        if err := c.Bind(&test); err != nil {            return err        }        log.Println("========================start io=====================")        time.Sleep(time.Duration(1) * time.Second)        log.Println("=========================end io=======================")        return c.JSON(http.StatusOK, test)    })    app.POST("/v3/test", func(c echo.Context) error {        var test Test2        if err := c.Bind(&test); err != nil {            return err        }        log.Println("========================start io=====================")        time.Sleep(time.Duration(1) * time.Second)        log.Println("=========================end io=======================")        return c.JSON(http.StatusOK, test)    })    app.POST("/v4/test", func(c echo.Context) error {        var test Test2        if err := c.Bind(&test); err != nil {            return err        }        log.Println("========================start io=====================")        time.Sleep(time.Duration(1) * time.Second)        log.Println("=========================end io=======================")        return c.JSON(http.StatusOK, test)    })    // Start server    app.Logger.Fatal(app.Start(":8080"))}
  1. 以上除了echo之外,其他三個都原生支援了jsoniter 這個效能的json序列化庫,都啟用。
  2. 等待1s,類比io讀寫等待

測試對比

由於要測試5種body樣本,4種情境,4個架構,因此把重點資料篩選出來(輸送量、錯誤率和99%Line,重要性依次遞減),結果都繪製了圖形,方便比對查看。

565bytes下測試結果



5KB下測試結果



10KB下測試結果



50KB下測試結果



100KB下測試結果



總結

綜合以上各個測試結果可以看出,gin以及iris都是非常優秀的架構,gin的優勢比其他稍微大點,iris次之,而echo相應差一點。
本次測試只是簡單測試了一下3個架構的並發和json相關。對比結果,不包括生態和工具的完善度等等。如果測試有什麼不完善的地方,歡迎交流。
另外歡迎大家試用和star另外一個web架構baa,為了避嫌我沒有貼出baa的資料,效能測試處於gin之後和iris之間。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.