Browse Source

Merge pull request #1740 from wetgi/master

ECHO Framework
blee-techempower 9 years ago
parent
commit
2e2eca56db

+ 14 - 0
frameworks/Go/echo/README.md

@@ -0,0 +1,14 @@
+# [ECHO](http://echo.labstack.com) (GoLang) Benchmarking Test
+
+This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
+
+"Echo is a fast and unfancy micro web framework for Go."
+
+## Test URLs
+
+    http://localhost:8080/json
+    http://localhost:8080/db
+    http://localhost:8080/queries?queries=[1-500]
+    http://localhost:8080/fortunes
+    http://localhost:8080/updates?queries=[1-500]
+    http://localhost:8080/plaintext

+ 28 - 0
frameworks/Go/echo/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "echo",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates/",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "ECHO",
+      "language": "Go",
+      "orm": "Raw",
+      "platform": "Go",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ECHO",
+      "notes": "",
+      "versus": "go"
+    }
+  }]
+}

+ 20 - 0
frameworks/Go/echo/fortune.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+</body>
+</html>

+ 0 - 0
frameworks/Go/echo/layout.html


+ 204 - 0
frameworks/Go/echo/server.go

@@ -0,0 +1,204 @@
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"html/template"
+	"io"
+	"log"
+	"math/rand"
+	"net/http"
+	"sort"
+	"strconv"
+
+	_ "github.com/go-sql-driver/mysql"
+	"github.com/labstack/echo"
+)
+
+const (
+	// Database
+	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world"
+	worldRowCount      = 10000
+	macIdleConnection  = 30
+	maxConnectionCount = 256
+
+	helloWorldString = "Hello, World!"
+	worldSelect      = "SELECT id, randomNumber FROM World WHERE id = ?"
+	worldUpdate      = "UPDATE World SET randomNumber = ? WHERE id = ?"
+	fortuneSelect    = "SELECT id, message FROM Fortune;"
+)
+
+var (
+	// Database
+	worldStatement   *sql.Stmt
+	fortuneStatement *sql.Stmt
+	updateStatement  *sql.Stmt
+)
+
+type Template struct {
+	templates *template.Template
+}
+
+type MessageStruct struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           uint16 `json:"id"`
+	RandomNumber uint16 `json:"randomNumber"`
+}
+
+func randomRow() *sql.Row {
+	return worldStatement.QueryRow(rand.Intn(worldRowCount) + 1)
+}
+
+type Fortune struct {
+	Id      uint16 `json:"id"`
+	Message string `json:"message"`
+}
+
+type Fortunes []*Fortune
+
+func (s Fortunes) Len() int      { return len(s) }
+func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type ByMessage struct{ Fortunes }
+
+func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
+
+// Render HTML
+func (t *Template) Render(w io.Writer, name string, data interface{}) error {
+	return t.templates.ExecuteTemplate(w, name, data)
+}
+
+// queries parameter between 1 and 500.
+func sanitizeQueryParam(param string) int {
+	queries, err := strconv.Atoi(param)
+	if err != nil || queries < 1 {
+		return 1
+	}
+	if queries > 500 {
+		return 500
+	}
+	return queries
+}
+
+func json(c *echo.Context) error {
+	return c.JSON(http.StatusOK, MessageStruct{"Hello, World!"})
+}
+
+func plaintext(c *echo.Context) error {
+	return c.String(http.StatusOK, "Hello, World!")
+}
+
+func fortunes(c *echo.Context) error {
+	rows, err := fortuneStatement.Query()
+	if err != nil {
+		log.Fatalf("Error preparing statement: %v", err)
+	}
+
+	fortunes := make(Fortunes, 0, 16)
+
+	for rows.Next() {
+		fortune := Fortune{}
+		if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
+			log.Fatalf("Error scanning fortune row: %s", err.Error())
+		}
+		fortunes = append(fortunes, &fortune)
+	}
+	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
+
+	sort.Sort(ByMessage{fortunes})
+	return c.Render(http.StatusOK, "fortune.html", fortunes)
+}
+
+func singleQuery(c *echo.Context) error {
+	world := World{}
+	if err := randomRow().Scan(&world.Id, &world.RandomNumber); err != nil {
+		log.Fatalf("Error scanning world row: %s", err.Error())
+	}
+	return c.JSON(http.StatusOK, world)
+}
+
+func multipleQueries(c *echo.Context) error {
+	// Get Param
+	queries := sanitizeQueryParam(c.Param("queries"))
+	worlds := make([]World, queries)
+	for i := 0; i < queries; i++ {
+		if err := randomRow().Scan(&worlds[i].Id, &worlds[i].RandomNumber); err != nil {
+			log.Fatalf("Error scanning world row: %s", err.Error())
+		}
+	}
+	return c.JSON(http.StatusOK, worlds)
+}
+
+func updates(c *echo.Context) error {
+	// Get Param
+	queries := sanitizeQueryParam(c.Param("queries"))
+	worlds := make([]World, queries)
+
+	for i := 0; i < queries; i++ {
+		if err := randomRow().Scan(&worlds[i].Id, &worlds[i].RandomNumber); err != nil {
+			log.Fatalf("Error scanning world row: %s", err.Error())
+		}
+		worlds[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
+		if _, err := updateStatement.Exec(worlds[i].RandomNumber, worlds[i].Id); err != nil {
+			log.Fatalf("Error updating world row: %s", err.Error())
+		}
+	}
+	return c.JSON(http.StatusOK, worlds)
+}
+
+func main() {
+	e := echo.New()
+
+	// Set Templates
+	tmpl := &Template{
+		templates: template.Must(template.ParseFiles("fortune.html", "layout.html")),
+	}
+	e.SetRenderer(tmpl)
+
+	// Middleware
+	e.Use(ServerHeader())
+
+	// Routes
+	e.Get("/json", json)
+	e.Get("/db", singleQuery)
+	e.Get("/queries/:queries", multipleQueries)
+	e.Get("/fortunes", fortunes)
+	e.Get("/updates/:queries", updates)
+	e.Get("/plaintext", plaintext)
+
+	// Start server
+	e.Run(":8080")
+}
+
+func ServerHeader() echo.MiddlewareFunc {
+	return func(h echo.HandlerFunc) echo.HandlerFunc {
+		return func(c *echo.Context) error {
+			c.Response().Header().Add("Server", "ECHO")
+			return h(c)
+		}
+	}
+}
+
+func init() {
+	db, err := sql.Open("mysql", fmt.Sprintf(connectionString))
+	if err != nil {
+		log.Fatalf("Error opening database: %v", err)
+	}
+	db.SetMaxIdleConns(maxConnectionCount)
+
+	worldStatement, err = db.Prepare(worldSelect)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fortuneStatement, err = db.Prepare(fortuneSelect)
+	if err != nil {
+		log.Fatal(err)
+	}
+	updateStatement, err = db.Prepare(worldUpdate)
+	if err != nil {
+		log.Fatal(err)
+	}
+}

+ 10 - 0
frameworks/Go/echo/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' server.go
+
+fw_depends go
+
+go get github.com/go-sql-driver/mysql
+go get github.com/labstack/echo
+
+go run server.go &

+ 2 - 1
frameworks/Go/goji/src/goji/server.go

@@ -6,6 +6,7 @@ import (
 	"flag"
 	"fmt"
 	"html/template"
+	"io/ioutil"
 	"log"
 	"math/rand"
 	"net/http"
@@ -190,7 +191,7 @@ func plaintext(c web.C, w http.ResponseWriter, r *http.Request) {
 
 func main() {
 	runtime.GOMAXPROCS(runtime.NumCPU())
-
+	log.SetOutput(ioutil.Discard) // add line 3
 	db, err := sql.Open("mysql", connectionString)
 	if err != nil {
 		log.Fatalf("Error opening database: %v", err)