Browse Source

Add GoFrame framework (#5603)

* add goframe

* fix typo

* fix dockerfile

* fix dockerfile

* travis updates

* fix dockerfile

* fix dockerfile2

* fix issue in /fortunes

* add readme.md
John Guo 5 years ago
parent
commit
c2f3f2bfc2

+ 1 - 0
.travis.yml

@@ -36,6 +36,7 @@ env:
     - 'TESTDIR="Go/chi Go/gin Go/goji Go/aah Go/beego Go/echo Go/gnet"'
     - 'TESTDIR="Go/falcore Go/fiber Go/kami Go/martini Go/revel Go/webgo"'
     - 'TESTDIR="Go/evio Go/fasthttp Go/go-std Go/atreugo Go/gramework"'
+    - 'TESTDIR="Go/goframe"'
     - "TESTLANG=Groovy"
     - "TESTDIR=Haskell/snap"
     - "TESTDIR=Haskell/yesod"

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

@@ -0,0 +1,14 @@
+# [goframe](https://github.com/gogf/gf) (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.
+
+"`GF(GoFrame)` is a modular, full-featured and production-ready application development framework of golang. Providing a series of core components and dozens of practical modules, such as: cache, logging, containers, timer, resource, validator, database orm, etc. Supporting web server integrated with router, cookie, session, middleware, logger, configure, template, https, hooks, rewrites and many more features."
+
+## 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/goframe/benchmark_config.json

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

+ 6 - 0
frameworks/Go/goframe/goframe.dockerfile

@@ -0,0 +1,6 @@
+FROM golang:1.14
+
+ADD     ./src /goframe
+WORKDIR /goframe
+RUN     go build -o main main.go
+CMD     ./main

+ 6 - 0
frameworks/Go/goframe/src/go.mod

@@ -0,0 +1,6 @@
+module goframe
+
+require (
+	github.com/gogf/gf latest
+	github.com/go-sql-driver/mysql v1.5.0
+)

+ 30 - 0
frameworks/Go/goframe/src/go.sum

@@ -0,0 +1,30 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
+github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/gf-third/yaml v1.0.1 h1:pqD4ix+65DqGphU1MDnToPZfGYk0tuuwRzuTSl3g0d0=
+github.com/gf-third/yaml v1.0.1/go.mod h1:t443vj0txEw3+E0MOtkr83kt+PrZg2I8SRuYfn85NM0=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gogf/gf v1.12.1 h1:DTwxiFIqCzrrlCHvMgOxkgMn41gPKxb9oVy4Nx7ahGs=
+github.com/gogf/gf v1.12.1/go.mod h1:5ItmC5B/St8P4hp0/waKmnvHwAfAJ1uspMvR2wUD4UA=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA=
+github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
+github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
+github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 197 - 0
frameworks/Go/goframe/src/main.go

@@ -0,0 +1,197 @@
+package main
+
+import (
+	"log"
+	"runtime"
+	"sort"
+
+	"database/sql"
+
+	_ "github.com/go-sql-driver/mysql"
+
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/grand"
+)
+
+type (
+	World struct {
+		Id           uint16 `json:"id"`
+		RandomNumber uint16 `json:"randomNumber"`
+	}
+
+	Fortune struct {
+		Id      uint16 `json:"id"`
+		Message string `json:"message"`
+	}
+
+	Fortunes []*Fortune
+
+	ByMessage struct {
+		Fortunes
+	}
+)
+
+func (s Fortunes) Len() int {
+	return len(s)
+}
+
+func (s Fortunes) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+func (s ByMessage) Less(i, j int) bool {
+	return s.Fortunes[i].Message < s.Fortunes[j].Message
+}
+
+const (
+	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
+)
+
+var (
+	worldStatement   *sql.Stmt
+	fortuneStatement *sql.Stmt
+	updateStatement  *sql.Stmt
+)
+
+func init() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+	db, err := sql.Open(
+		"mysql",
+		"benchmarkdbuser:benchmarkdbpass@tcp(tfb-database:3306)/hello_world",
+	)
+	if err != nil {
+		log.Fatalf("Error opening database: %v", err)
+	}
+	db.SetMaxIdleConns(maxConnectionCount)
+	if worldStatement, err = db.Prepare(worldSelect); err != nil {
+		log.Fatal(err)
+	}
+	if fortuneStatement, err = db.Prepare(fortuneSelect); err != nil {
+		log.Fatal(err)
+	}
+	if updateStatement, err = db.Prepare(worldUpdate); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func main() {
+	g.View().SetAutoEncode(true)
+	s := g.Server()
+	s.Group("/", func(group *ghttp.RouterGroup) {
+		group.Middleware(func(r *ghttp.Request) {
+			r.Middleware.Next()
+			r.Response.Header().Set("Server", "GoFrame")
+		})
+		group.GET("/db", handlerDb)
+		group.GET("/dbs", handlerDbs)
+		group.GET("/json", handlerJson)
+		group.GET("/update", handlerUpdate)
+		group.GET("/fortunes", handlerFortunes)
+		group.GET("/plaintext", handlerPlaintext)
+	})
+	s.SetPort(8080)
+	s.Run()
+}
+
+/// Test 1: JSON serialization
+func handlerJson(r *ghttp.Request) {
+	r.Response.WriteJson(g.Map{
+		"message": "Hello, World!",
+	})
+}
+
+/// Test 2: Single database query
+func handlerDb(r *ghttp.Request) {
+	var world World
+	err := worldStatement.QueryRow(grand.Intn(worldRowCount)+1).
+		Scan(
+			&world.Id,
+			&world.RandomNumber,
+		)
+	if err != nil {
+		r.Response.WriteStatusExit(500, err.Error())
+	}
+	r.Response.WriteJson(world)
+}
+
+/// Test 3: Multiple database queries
+func handlerDbs(r *ghttp.Request) {
+	var (
+		queries = parseQueries(r)
+		worlds  = make([]World, queries)
+	)
+	for i := 0; i < queries; i++ {
+		err := worldStatement.QueryRow(grand.Intn(worldRowCount)+1).
+			Scan(
+				&worlds[i].Id,
+				&worlds[i].RandomNumber,
+			)
+		if err != nil {
+			r.Response.WriteStatusExit(500, err.Error())
+		}
+	}
+	r.Response.WriteJson(worlds)
+}
+
+/// Test 4: Fortunes
+func handlerFortunes(r *ghttp.Request) {
+	rows, err := fortuneStatement.Query()
+	if err != nil {
+		r.Response.WriteStatusExit(500, err.Error())
+	}
+	fortunes := make(Fortunes, 0, 16)
+	for rows.Next() {
+		fortune := Fortune{}
+		if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
+			r.Response.WriteStatusExit(500, err.Error())
+		}
+		fortunes = append(fortunes, &fortune)
+	}
+	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
+	sort.Sort(ByMessage{fortunes})
+
+	r.Response.WriteTpl("fortune.html", g.Map{
+		"list": fortunes,
+	})
+}
+
+/// Test 5: Database updates
+func handlerUpdate(r *ghttp.Request) {
+	var (
+		queries = parseQueries(r)
+		world   = make([]World, queries)
+	)
+	for i := 0; i < queries; i++ {
+		if err := worldStatement.QueryRow(grand.Intn(worldRowCount)+1).Scan(
+			&world[i].Id,
+			&world[i].RandomNumber,
+		); err != nil {
+			r.Response.WriteStatusExit(500, err.Error())
+		}
+		world[i].RandomNumber = uint16(grand.Intn(worldRowCount) + 1)
+		if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
+			r.Response.WriteStatusExit(500, err.Error())
+		}
+	}
+	r.Response.WriteJson(world)
+}
+
+/// Test 6: plaintext
+func handlerPlaintext(r *ghttp.Request) {
+	r.Response.Write("Hello, World!")
+}
+
+func parseQueries(r *ghttp.Request) int {
+	n := r.GetQueryInt("queries")
+	if n < 1 {
+		n = 1
+	} else if n > 500 {
+		n = 500
+	}
+	return n
+}

+ 20 - 0
frameworks/Go/goframe/src/template/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 .list}}
+<tr>
+<td>{{.Id}}</td>
+<td>{{.Message}}</td>
+</tr>
+{{end}}
+</table>
+</body>
+</html>