Browse Source

Merge branch '835'

Alex Schneider 11 years ago
parent
commit
4406f58d07

+ 17 - 0
gorail/README.md

@@ -0,0 +1,17 @@
+# Gorail Core Benchmarking Test
+
+This is the Gorail Core portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+Gorail Core is a lightweight HTTP server framework, for those who don't want an over simplied framework
+
+### JSON Encoding Test
+* [JSON test source](src/hello/hello.go)
+
+## Versions
+
+* [Go 1.2](http://golang.org/)
+
+## Test URLs
+
+### JSON Encoding Test
+
+http://localhost:8080/json

+ 0 - 0
gorail/__init__.py


+ 28 - 0
gorail/benchmark_config

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

+ 2 - 0
gorail/setup.bat

@@ -0,0 +1,2 @@
+set GOPATH=C:\FrameworkBenchmarks\gorail
+go run src\hello\hello.go

+ 31 - 0
gorail/setup.py

@@ -0,0 +1,31 @@
+import subprocess
+import sys
+import os
+import setup_util
+
+def start(args, logfile, errfile):
+  setup_util.replace_text("go/src/hello/hello.go", "tcp\(.*:3306\)", "tcp(" + args.database_host + ":3306)")
+  if os.name == 'nt':
+    #subprocess.call("rmdir /s /q pkg\\windows_amd64", shell=True, cwd="go")
+    #subprocess.call("rmdir /s /q src\\github.com", shell=True, cwd="go")
+    #subprocess.call("del /s /q /f bin\\hello.exe", shell=True, cwd="go")
+    subprocess.call("set GOPATH=C:\\FrameworkBenchmarks\\gorail&& go get ./...", shell=True, cwd="go", stderr=errfile, stdout=logfile)
+    subprocess.Popen("setup.bat", shell=True, cwd="go", stderr=errfile, stdout=logfile) 
+    return 0
+  os.environ["GOPATH"] = os.path.expanduser('~/FrameworkBenchmarks/gorail')
+  subprocess.call("go get -u github.com/gorail/core", shell=True, cwd="gorail", stderr=errfile, stdout=logfile)
+  subprocess.call("go get ./...", shell=True, cwd="go", stderr=errfile, stdout=logfile) 
+  subprocess.Popen("go run src/hello/hello.go".rsplit(" "), cwd="go", stderr=errfile, stdout=logfile)
+  return 0
+def stop(logfile, errfile):
+  if os.name == 'nt':
+    subprocess.call("taskkill /f /im go.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call("taskkill /f /im hello.exe > NUL", shell=True, stderr=errfile, stdout=logfile)
+    return 0
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'hello' in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 15)
+  return 0

+ 6 - 0
gorail/source_code

@@ -0,0 +1,6 @@
+./go/src/
+./go/src/hello
+./go/src/hello/hello.go
+./go/templates/
+./go/templates/fortune.html
+./go/templates/layout.html

+ 199 - 0
gorail/src/hello/hello.go

@@ -0,0 +1,199 @@
+package main
+
+import (
+	"database/sql"
+	"github.com/gorail/core"
+	"html/template"
+	"log"
+	"math/rand"
+	"net/http"
+	"runtime"
+	"sort"
+	"strconv"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+type Message struct {
+	Message string `json:"message"`
+}
+
+type World struct {
+	Id           uint16 `json:"id"`
+	RandomNumber uint16 `json:"randomNumber"`
+}
+
+type Fortune struct {
+	Id      uint16 `json:"id"`
+	Message string `json:"message"`
+}
+
+const (
+	// Database
+	connectionString   = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world"
+	worldSelect        = "SELECT id, randomNumber FROM World WHERE id = ?"
+	worldUpdate        = "UPDATE World SET randomNumber = ? WHERE id = ?"
+	fortuneSelect      = "SELECT id, message FROM Fortune;"
+	worldRowCount      = 10000
+	maxConnectionCount = 256
+
+	helloWorldString = "Hello, World!"
+)
+
+var (
+	// Templates
+	tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))
+
+	// Database
+	worldStatement   *sql.Stmt
+	fortuneStatement *sql.Stmt
+	updateStatement  *sql.Stmt
+
+	helloWorldBytes = []byte(helloWorldString)
+)
+
+func main() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+
+	db, err := sql.Open("mysql", 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)
+	}
+
+	app := core.NewApp()
+
+	app.DefaultRouter = app.DirRouter("main").RegisterFuncMap(core.FuncMap{
+		"db":        dbHandler,
+		"queries":   queriesHandler,
+		"json":      jsonHandler,
+		"fortune":   fortuneHandler,
+		"update":    updateHandler,
+		"plaintext": plaintextHandler,
+	})
+
+	app.Listen(":8080")
+}
+
+// Test 1: JSON serialization
+func jsonHandler(c *core.Context) {
+	c.Res.Header().Set("Content-Type", "application/javascript")
+	c.Json().Send(&Message{helloWorldString})
+}
+
+// Test 2: Single database query
+func dbHandler(c *core.Context) {
+	var world World
+	err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
+	if err != nil {
+		log.Fatalf("Error scanning world row: %s", err.Error())
+	}
+
+	c.Res.Header().Set("Content-Type", "application/json")
+	c.Json().Send(&world)
+}
+
+// Test 3: Multiple database queries
+func queriesHandler(c *core.Context) {
+	n := 1
+	if nStr := c.Req.URL.Query().Get("queries"); len(nStr) > 0 {
+		n, _ = strconv.Atoi(nStr)
+	}
+
+	if n <= 1 {
+		dbHandler(c)
+		return
+	}
+
+	world := make([]World, n)
+	for i := 0; i < n; i++ {
+		err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
+		if err != nil {
+			log.Fatalf("Error scanning world row: %s", err.Error())
+		}
+	}
+
+	c.Res.Header().Set("Content-Type", "application/json")
+	c.Json().Send(world)
+}
+
+// Test 4: Fortunes
+func fortuneHandler(c *core.Context) {
+	rows, err := fortuneStatement.Query()
+	if err != nil {
+		log.Fatalf("Error preparing statement: %v", err)
+	}
+
+	fortunes := make(Fortunes, 0, 16)
+	for rows.Next() { //Fetch rows
+		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})
+	c.Res.Header().Set("Content-Type", "text/html")
+	if err := tmpl.Execute(c.Res, fortunes); err != nil {
+		http.Error(c.Res, err.Error(), http.StatusInternalServerError)
+	}
+}
+
+// Test 5: Database updates
+func updateHandler(c *core.Context) {
+	n := 1
+	if nStr := c.Req.URL.Query().Get("queries"); len(nStr) > 0 {
+		n, _ = strconv.Atoi(nStr)
+	}
+
+	c.Res.Header().Set("Content-Type", "application/json")
+
+	if n <= 1 {
+		var world World
+		worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
+		world.RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
+		updateStatement.Exec(world.RandomNumber, world.Id)
+		c.Json().Send(&world)
+	} else {
+		world := make([]World, n)
+		for i := 0; i < n; i++ {
+			if err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
+				log.Fatalf("Error scanning world row: %s", err.Error())
+			}
+			world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
+			if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
+				log.Fatalf("Error updating world row: %s", err.Error())
+			}
+		}
+		c.Json().Send(world)
+	}
+}
+
+// Test 6: Plaintext
+func plaintextHandler(c *core.Context) {
+	c.Res.Header().Set("Content-Type", "text/plain")
+	c.Res.Write(helloWorldBytes)
+}
+
+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 }

+ 14 - 0
gorail/templates/fortune.html

@@ -0,0 +1,14 @@
+{{define "content"}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{range .}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+{{end}}

+ 9 - 0
gorail/templates/layout.html

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+{{template "content" .}}
+</body>
+</html>