preface
Because Golang provides a complete NET/HTTP standard library, which is much easier to implement a Web framework based on than other languages, the Go Web framework is like a thousand flowers. From the old ones like Revel and Beego, to newer ones like Gin, Iris, and others, there are also some routers like Chi. Individual small projects, especially middleware, that need to expose some HTTP interface, basically use CHI. This test mainly focuses on the three frameworks of Gin Iris Echo. The focus is on high performance, measured in terms of concurrency and JSON serialization and deserialization, which is what background projects focus on.
test
Test Environment description
To select an I/O heavy framework, configure demo in the following scenarios. The demo requirements are as follows:
- Enable the log function (logs are also recorded when normal services are simulated) to record a log at the start and end of a request
- The interface is paused with sleep for 1 second, assuming the network IO operation here (and it is easier to see from the log whether coroutine concurrent behavior)
- Use the POST interface to test, no processing is done in the interface, the body received directly serialized back (serialization and deserialization are the framework’s most frequent actions)
- Open accessLog for the framework
The test tools and scenarios are as follows
- The test tool uses classic JMeter and tests directly using a GUI interface
- Scenarios are divided into 10 threads, 100 threads, 500 threads, 1000 threads, and 1500 threads
- All results look at jMeter’s aggregated reports, focusing on throughput, time, and number of errors
- All demos start with a single thread. The asynchronous framework does not limit the number of coroutines. GOMAXPROCS=1
- All tests were performed locally and the pressure test lasted two minutes
- POST requests were used for testing, and data samples were 565kb, 5KB, 10KB, 50KB, and 100KB, each of which was tested on a different concurrent thread
The test code
gin:
package main import ( "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 test2 type 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}Copy the code
iris:
package main import ( "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 test2 type 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) }Copy the code
echo:
package main import ( "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 test2 type 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")) }Copy the code
- In addition to echo, the other three natively support the performance of jsoniter json serialization library, are enabled.
- The analog I/O reading and writing wait for 1s
Contrast test
As 5 body samples, 4 scenarios and 4 frames need to be tested, the key data (throughput, error rate and 99%Line, in descending order of importance) are screened out, and the results are all graphed for easy comparison and inspection.
The test result is 565bytes
Test results under 5KB
Test results in 10KB
Test results under 50KB
Test results under 100KB
conclusion
Based on the above test results, it can be seen that gin and IRIS are both excellent frameworks. Gin has a slightly greater advantage than other frameworks, followed by IRIS and inferior to Echo. This test simply tested the concurrency and JSON correlation of the three frameworks. Comparison results, not including ecology, tool perfection, etc. If there are any imperfections in the test, please feel free to communicate. In addition, you are welcome to try baA, another Web framework with STAR. In order to avoid suspicion, I did not post baA data, and the performance test is between GIN and IRIS.