initial
This commit is contained in:
commit
8878c5e89c
130
README.md
Normal file
130
README.md
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Sux
|
||||||
|
|
||||||
|
Static route http router that considers the request method
|
||||||
|
|
||||||
|
Useful for serving server-side rendered content.
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.icod.de/dalu/sux"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := sux.New()
|
||||||
|
r.GET("/", Hello)
|
||||||
|
r.GET("/simple", Simple)
|
||||||
|
http.ListenAndServe(":8080", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hello(w http.ResponseWriter, r *http.Request) {
|
||||||
|
io.WriteString(w, "hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Simple(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
io.WriteString(w, "hello "+name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
```
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
|
||||||
|
Running 30s test @ http://inuc:8080/
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 7.08ms 3.02ms 64.38ms 89.40%
|
||||||
|
Req/Sec 18.24k 2.11k 24.06k 73.88%
|
||||||
|
4260394 requests in 30.00s, 491.63MB read
|
||||||
|
Requests/sec: 142025.60
|
||||||
|
Transfer/sec: 16.39MB
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple?name=Darko
|
||||||
|
Running 30s test @ http://inuc:8080/simple?name=Darko
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 8.11ms 3.84ms 243.61ms 89.96%
|
||||||
|
Req/Sec 16.01k 2.20k 23.88k 72.87%
|
||||||
|
3723315 requests in 30.00s, 454.51MB read
|
||||||
|
Requests/sec: 124116.94
|
||||||
|
Transfer/sec: 15.15MB
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Compared to https://github.com/julienschmidt/httprouter
|
||||||
|
```
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
|
||||||
|
Running 30s test @ http://inuc:8080/
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 7.14ms 3.13ms 74.06ms 89.99%
|
||||||
|
Req/Sec 18.18k 2.19k 23.31k 75.88%
|
||||||
|
4224358 requests in 30.00s, 487.47MB read
|
||||||
|
Requests/sec: 140826.15
|
||||||
|
Transfer/sec: 16.25MB
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple/Darko
|
||||||
|
Running 30s test @ http://inuc:8080/simple/Darko
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 7.78ms 3.40ms 60.03ms 88.79%
|
||||||
|
Req/Sec 16.55k 1.86k 19.95k 66.95%
|
||||||
|
3873629 requests in 30.00s, 472.86MB read
|
||||||
|
Requests/sec: 129131.98
|
||||||
|
Transfer/sec: 15.76MB
|
||||||
|
```
|
||||||
|
|
||||||
|
Compared to https://github.com/gocraft/web
|
||||||
|
```
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
|
||||||
|
Running 30s test @ http://inuc:8080/
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 9.07ms 4.58ms 420.89ms 91.40%
|
||||||
|
Req/Sec 14.42k 2.28k 25.00k 80.03%
|
||||||
|
3309152 requests in 30.00s, 378.70MB read
|
||||||
|
Requests/sec: 110315.36
|
||||||
|
Transfer/sec: 12.62MB
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple
|
||||||
|
Running 30s test @ http://inuc:8080/simple
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 9.86ms 4.00ms 66.98ms 87.79%
|
||||||
|
Req/Sec 13.05k 1.65k 17.52k 69.71%
|
||||||
|
3055899 requests in 30.00s, 375.95MB read
|
||||||
|
Requests/sec: 101874.46
|
||||||
|
Transfer/sec: 12.53MB
|
||||||
|
```
|
||||||
|
|
||||||
|
Compared to https://github.com/gomango/mux
|
||||||
|
```
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
|
||||||
|
Running 30s test @ http://inuc:8080/
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 9.22ms 12.73ms 235.88ms 84.36%
|
||||||
|
Req/Sec 13.78k 1.77k 25.52k 74.17%
|
||||||
|
3242004 requests in 30.00s, 377.20MB read
|
||||||
|
Socket errors: connect 0, read 0, write 0, timeout 10
|
||||||
|
Requests/sec: 108078.84
|
||||||
|
Transfer/sec: 12.57MB
|
||||||
|
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple
|
||||||
|
Running 30s test @ http://inuc:8080/simple
|
||||||
|
8 threads and 1000 connections
|
||||||
|
Thread Stats Avg Stdev Max +/- Stdev
|
||||||
|
Latency 16.24ms 14.39ms 150.41ms 56.30%
|
||||||
|
Req/Sec 7.77k 593.31 9.78k 68.20%
|
||||||
|
1841839 requests in 30.00s, 226.59MB read
|
||||||
|
Requests/sec: 61402.97
|
||||||
|
Transfer/sec: 7.55MB
|
||||||
|
```
|
||||||
|
|
||||||
|
Apples and Oranges but not quite
|
||||||
|
|
||||||
|
Hardware
|
||||||
|
|
||||||
|
http://www.intel.com/content/www/us/en/nuc/nuc-kit-d54250wykh.html
|
128
sux.go
Normal file
128
sux.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package sux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nomatch uint8 = iota
|
||||||
|
static
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
node struct {
|
||||||
|
term byte
|
||||||
|
ntype uint8
|
||||||
|
handler http.HandlerFunc
|
||||||
|
child []*node
|
||||||
|
}
|
||||||
|
Router struct {
|
||||||
|
route map[string]*node
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var root *Router
|
||||||
|
|
||||||
|
func makenode(term byte) *node {
|
||||||
|
return &node{
|
||||||
|
term: term,
|
||||||
|
child: make([]*node, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) addchild(term byte) *node {
|
||||||
|
if fn := n.findchild(term); fn == nil {
|
||||||
|
nn := makenode(term)
|
||||||
|
n.child = append(n.child, nn)
|
||||||
|
return nn
|
||||||
|
} else {
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) maketree(word []byte, handler http.HandlerFunc) {
|
||||||
|
m := n
|
||||||
|
for i, l := 1, len(word); i < l; i++ {
|
||||||
|
m = m.addchild(word[i])
|
||||||
|
}
|
||||||
|
m.ntype = static
|
||||||
|
m.handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) findchild(term byte) *node {
|
||||||
|
for _, v := range n.child {
|
||||||
|
if v.term == term {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) find(word string) *node {
|
||||||
|
ss := []byte(word)
|
||||||
|
m := n
|
||||||
|
for i, l := 1, len(ss); i < l; i++ {
|
||||||
|
m = m.findchild(ss[i])
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if m, exists := n.route[r.Method]; exists {
|
||||||
|
m = m.find(r.URL.Path)
|
||||||
|
if m == nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.ntype == nomatch {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.handler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Router) GET(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["GET"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) POST(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["POST"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) PUT(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["PUT"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) PATCH(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["PATCH"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) DELETE(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["DELETE"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) OPTIONS(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["OPTIONS"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func (n *Router) HEAD(path string, handler http.HandlerFunc) *Router {
|
||||||
|
n.route["HEAD"].maketree([]byte(path), handler)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *Router {
|
||||||
|
root = &Router{route: make(map[string]*node)}
|
||||||
|
root.route["GET"] = makenode([]byte("/")[0])
|
||||||
|
root.route["POST"] = makenode([]byte("/")[0])
|
||||||
|
root.route["PUT"] = makenode([]byte("/")[0])
|
||||||
|
root.route["PATCH"] = makenode([]byte("/")[0])
|
||||||
|
root.route["DELETE"] = makenode([]byte("/")[0])
|
||||||
|
root.route["OPTIONS"] = makenode([]byte("/")[0])
|
||||||
|
root.route["HEAD"] = makenode([]byte("/")[0])
|
||||||
|
return root
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user