Explorar o código

Add a new framework - gnet (#5317)

Andy Pan %!s(int64=5) %!d(string=hai) anos
pai
achega
26fb469c60

+ 1 - 1
.travis.yml

@@ -33,7 +33,7 @@ env:
     - "TESTLANG=Elixir"
     - "TESTLANG=Erlang"
     - "TESTLANG=FSharp"
-    - 'TESTDIR="Go/chi Go/gin Go/goji Go/aah Go/beego Go/echo"'
+    - 'TESTDIR="Go/chi Go/gin Go/goji Go/aah Go/beego Go/echo Go/gnet"'
     - 'TESTDIR="Go/falcore Go/kami Go/martini Go/revel Go/webgo"'
     - 'TESTDIR="Go/evio Go/fasthttp Go/go-std Go/atreugo Go/gramework"'
     - "TESTLANG=Groovy"

+ 9 - 0
frameworks/Go/gnet/README.md

@@ -0,0 +1,9 @@
+# [gnet](https://github.com/panjf2000/gnet) (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.
+
+"gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go."
+
+## Test URLs
+
+    http://localhost:8080/plaintext

+ 23 - 0
frameworks/Go/gnet/benchmark_config.json

@@ -0,0 +1,23 @@
+{
+  "framework": "gnet",
+  "tests": [{
+    "default": {
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Go",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Gnet",
+      "notes": "",
+      "versus": "go"
+    }
+  }]
+}

+ 13 - 0
frameworks/Go/gnet/gnet.dockerfile

@@ -0,0 +1,13 @@
+FROM golang:1.13
+
+ENV GO111MODULE on
+
+WORKDIR /gnet
+
+COPY ./src /gnet
+
+RUN go mod download
+
+RUN go build -o app -gcflags="-l=4" -ldflags="-s -w" .
+
+CMD ./app

+ 11 - 0
frameworks/Go/gnet/src/go.mod

@@ -0,0 +1,11 @@
+module gnet
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/kr/pretty v0.1.0 // indirect
+	github.com/panjf2000/gnet v1.0.0-rc.4
+	github.com/stretchr/testify v1.4.0 // indirect
+	golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 // indirect
+	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+	gopkg.in/yaml.v2 v2.2.7 // indirect
+)

+ 36 - 0
frameworks/Go/gnet/src/go.sum

@@ -0,0 +1,36 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw=
+github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
+github.com/panjf2000/ants/v2 v2.2.2 h1:TWzusBjq/IflXhy+/S6u5wmMLCBdJnB9tPIx9Zmhvok=
+github.com/panjf2000/ants/v2 v2.2.2/go.mod h1:1GFm8bV8nyCQvU5K4WvBCTG1/YBFOD2VzjffD8fV55A=
+github.com/panjf2000/gnet v1.0.0-rc.4 h1:kXOXCC60BkSd1OObcPGoPu+6EQbomL73RF+CHXS1DKI=
+github.com/panjf2000/gnet v1.0.0-rc.4/go.mod h1:OYcdgCx4Vhn5Tbrslc0TR3h0hwuCgSPyzyOCIxMRyFM=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/smallnest/goframe v0.0.0-20191101094441-1fbd8e51db18 h1:DiCiqE5WT7O6l4naQB0x6GBo8BEE2dBT7uhGFvGo0B0=
+github.com/smallnest/goframe v0.0.0-20191101094441-1fbd8e51db18/go.mod h1:Dy8560GXrB6w5OJnVBU71dJtSyINdnqHHe6atDaZX00=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2YhuY9zevDG2ORt5qBUI=
+golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 198 - 0
frameworks/Go/gnet/src/main.go

@@ -0,0 +1,198 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/panjf2000/gnet"
+)
+
+var res string
+var resBytes []byte
+
+type request struct {
+	proto, method string
+	path, query   string
+	head, body    string
+	remoteAddr    string
+}
+
+type httpServer struct {
+	*gnet.EventServer
+}
+
+var errMsg = "Internal Server Error"
+var errMsgBytes = []byte(errMsg)
+
+type httpCodec struct {
+	req request
+}
+
+func (hc *httpCodec) Encode(c gnet.Conn, buf []byte) (out []byte, err error) {
+	if c.Context() == nil {
+		return appendHandle(out, res), nil
+	}
+	return appendResp(out, "500 Error", "", errMsg+"\n"), nil
+}
+
+func (hc *httpCodec) Decode(c gnet.Conn) ([]byte, error) {
+	buf := c.Read()
+	// process the pipeline
+	leftover, err := parseReq(buf, &hc.req)
+	// bad thing happened
+	if err != nil {
+		c.SetContext(err)
+		return nil, err
+	} else if len(leftover) == len(buf) {
+		// request not ready, yet
+		return nil, nil
+	}
+	c.ResetBuffer()
+	return buf, nil
+}
+
+func (hs *httpServer) OnInitComplete(srv gnet.Server) (action gnet.Action) {
+	log.Printf("HTTP server is listening on %s (multi-cores: %t, loops: %d)\n",
+		srv.Addr.String(), srv.Multicore, srv.NumLoops)
+	return
+}
+
+func (hs *httpServer) React(c gnet.Conn) (out []byte, action gnet.Action) {
+	data := c.ReadFrame()
+	// process the pipeline
+	if c.Context() != nil {
+		// bad thing happened
+		out = errMsgBytes
+		action = gnet.Close
+		return
+	} else if data == nil {
+		// request not ready, yet
+		return
+	}
+	// handle the request
+	out = resBytes
+	return
+}
+
+func main() {
+	var port int
+	var multicore bool
+
+	// Example command: go run main.go --port 8080 --multicore true
+	flag.IntVar(&port, "port", 8080, "server port")
+	flag.BoolVar(&multicore, "multicore", true, "multicore")
+	flag.Parse()
+
+	res = "Hello, World!"
+	resBytes = []byte(res)
+
+	http := new(httpServer)
+	hc := new(httpCodec)
+
+	// Start serving!
+	log.Fatal(gnet.Serve(http, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithCodec(hc)))
+}
+
+// appendHandle handles the incoming request and appends the response to
+// the provided bytes, which is then returned to the caller.
+func appendHandle(b []byte, res string) []byte {
+	return appendResp(b, "200 OK", "", res)
+}
+
+// appendResp will append a valid http response to the provide bytes.
+// The status param should be the code plus text such as "200 OK".
+// The head parameter should be a series of lines ending with "\r\n" or empty.
+func appendResp(b []byte, status, head, body string) []byte {
+	b = append(b, "HTTP/1.1"...)
+	b = append(b, ' ')
+	b = append(b, status...)
+	b = append(b, '\r', '\n')
+	b = append(b, "Server: gnet\r\n"...)
+	b = append(b, "Content-Type: text/plain\r\n"...)
+	b = append(b, "Date: "...)
+	b = time.Now().AppendFormat(b, "Mon, 02 Jan 2006 15:04:05 GMT")
+	b = append(b, '\r', '\n')
+	if len(body) > 0 {
+		b = append(b, "Content-Length: "...)
+		b = strconv.AppendInt(b, int64(len(body)), 10)
+		b = append(b, '\r', '\n')
+	}
+	b = append(b, head...)
+	b = append(b, '\r', '\n')
+	if len(body) > 0 {
+		b = append(b, body...)
+	}
+	return b
+}
+
+// parseReq is a very simple http request parser. This operation
+// waits for the entire payload to be buffered before returning a
+// valid request.
+func parseReq(data []byte, req *request) (leftover []byte, err error) {
+	sdata := string(data)
+	var i, s int
+	var head string
+	var clen int
+	var q = -1
+	// method, path, proto line
+	for ; i < len(sdata); i++ {
+		if sdata[i] == ' ' {
+			req.method = sdata[s:i]
+			for i, s = i+1, i+1; i < len(sdata); i++ {
+				if sdata[i] == '?' && q == -1 {
+					q = i - s
+				} else if sdata[i] == ' ' {
+					if q != -1 {
+						req.path = sdata[s:q]
+						req.query = req.path[q+1 : i]
+					} else {
+						req.path = sdata[s:i]
+					}
+					for i, s = i+1, i+1; i < len(sdata); i++ {
+						if sdata[i] == '\n' && sdata[i-1] == '\r' {
+							req.proto = sdata[s:i]
+							i, s = i+1, i+1
+							break
+						}
+					}
+					break
+				}
+			}
+			break
+		}
+	}
+	if req.proto == "" {
+		return data, fmt.Errorf("malformed request")
+	}
+	head = sdata[:s]
+	for ; i < len(sdata); i++ {
+		if i > 1 && sdata[i] == '\n' && sdata[i-1] == '\r' {
+			line := sdata[s : i-1]
+			s = i + 1
+			if line == "" {
+				req.head = sdata[len(head)+2 : i+1]
+				i++
+				if clen > 0 {
+					if len(sdata[i:]) < clen {
+						break
+					}
+					req.body = sdata[i : i+clen]
+					i += clen
+				}
+				return data[i:], nil
+			}
+			if strings.HasPrefix(line, "Content-Length:") {
+				n, err := strconv.ParseInt(strings.TrimSpace(line[len("Content-Length:"):]), 10, 64)
+				if err == nil {
+					clen = int(n)
+				}
+			}
+		}
+	}
+	// not enough data
+	return data, nil
+}