erouter

Erouter is a high performance and high scale HTTP routing library with zero memory replication, strict routing order, low code complexity, group routing, middleware functionality, default parameters, constant matching, variable matching, wildcard matching, variable verification matching, wildcard verification matching, and host-based routing.

Design specification

Based on eudore framework routing separation, middleware mechanism was modified and MVC was removed.

RouterRadix

RouterRadix is implemented using cardinality tree, with zero memory replication, strict routing matching order, group routing, middleware functions, default parameters, constant matching, variable matching, wildcard matching functions.

example:

package main

import "log"
import "net/http"
import "github.com/eudore/erouter"

func main(a) {
	router := erouter.NewRouterRadix()
	router.AddMiddleware("ANY"."".func(h erouter.Handler) erouter.Handler {
		return func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
			log.Printf("%s %s route: %s", r.Method, r.URL.Path, p.GetParam("route"))
			h(w, r, p)
		}
	})
	router.Any("/ *".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("hello\n"))
	})
	router.Get("/api/*action version=v0".func(w http.ResponseWriter, _ *http.Request, p erouter.Params) {
		w.Write([]byte("access api " + p.GetParam("version") +":" + p.GetParam("action") + "\n"))
	})
	router.Get("/api/v1/*action version=v1".func(w http.ResponseWriter, _ *http.Request, p erouter.Params) {
		w.Write([]byte("access api " + p.GetParam("version") +":" + p.GetParam("action") + "\n"))
	})
	apiv2 := router.Group("/api/v2 version=v2")
	apiv2.Any("/*action".func(w http.ResponseWriter, _ *http.Request, p erouter.Params) {
		w.Write([]byte("access api " + p.GetParam("version") +":" + p.GetParam("action") + "\n"))
	})
	http.ListenAndServe(": 8080", router)
}
Copy the code

Test command:

The curl 127.0.0.1:8080 / get curl 127.0.0.1:8080 / API/getuser curl 127.0.0.1:8080 / API/v1 / getuser curl 127.0.0.1:8080 / API/v2 / getuserCopy the code

RouterFull

RouterFull is based on the RouterRadix extension to implement variable check matching and wildcard check matching functions.

Usage: after the normal variable and a wildcard, use ‘|’ symbol segmentation, for after checking rules, isnum is check function; Min :100 is the dynamic check function, min is the name of the dynamic check function, and ‘:’ is the parameter. If ‘^’ begins with the regular check and ends with ‘$’.

Note: Do not use Spaces in regular expressions, as this can lead to error parameter cutting. Use \u002 instead of Spaces.

:num|isnum
:num|min:100
:num|^0.*$
*num|isnum
*num|min:100
*num|^0.*$
Copy the code

example:

package main

import "net/http"
import "github.com/eudore/erouter"

func main(a) {
	router := erouter.NewRouterFull()
	router.Any("/ *".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("hello\n"))
	})
	router.Get("/:num|^0.*$".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("first char is '0', num is: " + p.GetParam("num") + "\n"))
	})
	router.Get("/:num|min:100".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("num great 100, num is: " + p.GetParam("num") + "\n"))
	})
	router.Get("/:num|isnum".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("num is: " + p.GetParam("num") + "\n"))
	})
	router.Get("/*var|^E.*$".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("first char is 'E', var is: " + p.GetParam("var") + "\n"))
	})
	http.ListenAndServe(": 8080", router)
}
Copy the code

Test command:

Do you want to use the following curl to do something: 1 127.0.0.1:8080 / Erouter / 123Copy the code

RouterHost

RouterHost implements host-based routing by registering and matching sub-routers corresponding to hosts.

Currently, traversal matches are used, and the Host matching function is path.Match. In the future, only ‘*’ wildcards and constants are reserved for matching rules.

Usage: To register a sub-router under the rule of domain name registration for the Host router, add a route using the Host parameter to match the registered router, and use the requested Host to match the registered route.

example:

package main

import "net/http"
import "github.com/eudore/erouter"

func main(a) {
	router := erouter.NewRouterHost().(*erouter.RouterHost)
	router.RegisterHost("*.example.com", erouter.NewRouterRadix())
	router.Any("/ *".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("hello\n"))
	})
	router.Get("/* host=*.example.com".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		w.Write([]byte("host is " + r.Host + ", match host: " + p.GetParam("host") + "\n"))
	})
	http.ListenAndServe(": 8080", router)
}
Copy the code

Test command:

Curl 127.0.0.1:8080 curl -xput 127.0.0.1:8080 curl -h'Host: www.example.com'127.0.0.1: curl - 8080 H'Host: www.example.com'The curl - H - XPUT 127.0.0.1:8080'Host: www.example.com'- Xput 127.0.0.1:8080Copy the code

Benchmark

Using GithubApi, Erouter achieves 60% of httprOUTER’s matching performance, but has strict route matching sequence, easy to extend and rewrite, and low code complexity.

Test command:

go get github.com/eudore/web-framework-benchmark
go test -bench=router github.com/eudore/web-framework-benchmark
Copy the code

Test results:

goos: linux goarch: amd64 pkg: github.com/eudore/web-framework-benchmark BenchmarkHttprouterStatic-2 50000 25518 ns/op 1949 B/op 157 allocs/op BenchmarkHttprouterGitHubAPI-2 30000 57961 ns/op 16571 B/op 370 allocs/op BenchmarkHttprouterGplusAPI-2 500000 2747 ns/op 813 B/op 24 allocs/op BenchmarkHttprouterParseAPI-2 300000 3886 ns/op 963 B/op 42 allocs/op BenchmarkErouterRadixStatic-2 30000 44147 ns/op 2412 B/op 157 allocs/op BenchmarkErouterRadixGitHubAPI-2 20000 63756 ns/op 2501 B/op 203 allocs/op BenchmarkErouterRadixGplusAPI-2 500000 2653 ns/op 173 B/op 13 allocs/op BenchmarkErouterRadixParseAPI-2 300000 4523 ns/op 323 B/op 26 allocs/op BenchmarkErouterFullStatic-2 30000 43923 ns/op 2413 B/op 157 allocs/op BenchmarkErouterFullGitHubAPI-2 20000 65698 ns/op 2503 B/op 203 allocs/op BenchmarkErouterFullGplusAPI-2 500000 2582 ns/op 173 B/op 13 allocs/op BenchmarkErouterFullParseAPI-2 300000 5990 ns/op 323 B/ OP 26 Allocs/OP PASS OK github.com/eudore/web-framework-benchmark 19.934sCopy the code

Api

The main methods used are listed in the reference documentation.

Router interface definition:

type (
	// Params read/write request processing parameters.
	Params interface {
		GetParam(string) string
		AddParam(string.string)
		SetParam(string.string)}// Erouter handles a request method, adding Parmas to http.handlerfunc.
	Handler func(http.ResponseWriter, *http.Request, Params)// Define a request processing middleware function that uses decorator assembly to process the request by passing in a processing and then returning a processing.Middleware func(Handler) Handler
	// The route is directly registered by default. Other methods can be directly registered using the RouterRegister interface// // Routes are directly registered by default. Other routes can be registered by defaultRouterRegisterInterface direct registration.RouterMethod interface {
		Group(string) RouterMethod
		AddHandler(string.string, Handler) RouterMethod
		AddMiddleware(string.string. Middleware) RouterMethod NotFound(Handler) MethodNotAllowed(Handler) Any(string, Handler)
		Delete(string, Handler)
		Get(string, Handler)
		Head(string, Handler)
		Options(string, Handler)
		Patch(string, Handler)
		Post(string, Handler)
		Put(string, Handler)
	}
	// Router core interface, performing routing, middleware registration, and processing http requests.
	//
	// The router core interface performs routing, middleware registration, and processing HTTP requests.
	RouterCore interface {
		RegisterMiddleware(string.string, []Middleware)
		RegisterHandler(string.string, Handler)
		ServeHTTP(http.ResponseWriter, *http.Request)
	}
	// The router interface needs to implement two methods: the router method and the router core.
	//
	// Implement router method and router core interfaces.
	Router interface {
		RouterCore
		RouterMethod
	}
)
Copy the code

NewRouter

There are currently three implementations, each of which implements the Router interface.

func NewRouterRadix() Router
func NewRouterFull() Router
func NewRouterHost() Router
Copy the code
router1 := erouter.NewRouterRadix()
router2 := erouter.NewRouterFull()
router3 := erouter.NewRouterHost()
Copy the code

Group

func Group(path string) RouterMethod

Group Implements router grouping.

router := erouter.NewRouterRadix()
apiv1 := router.Group("/api/v1 version=v1")
apiv1.Get("/ *",...).Copy the code

AddHandler

func AddHandler(method string, path string, handler Handler) RouterMethod

AddHandler is used to add a new route.

router := erouter.NewRouterRadix()
router.AddHandle("GET"."/ *",...).Copy the code

AddMiddleware

func AddMiddleware(method string, path string, midds ... Middleware) RouterMethod

AddMiddleware adds processing middleware to the current routing method.

router := erouter.NewRouterRadix()
router.AddMiddleware("ANY"."".func(h erouter.Handler) erouter.Handler {
	return func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
		/ / befor.// Call next to handle the summary function
		h(w, r, p)
		/ / after execution. }})Copy the code

NotFound

func NotFound(Handler)

Set up router 404 processing.

MethodNotAllowed

func MethodNotAllowed(Handler)

Set router 405 processing.

Any

func Any(path string, handler Handler)

Register the Any method, equivalent to AddHandler’s method “Any”.

The set of Any methods is erouter.RouterAllMethod. The extended new methods Radix and Full are not supported.

Get

func Get(path string, handler Handler)

Register the Get method, equivalent to AddHandler method “Get”, post, put and other methods function similar.

router := erouter.NewRouterRadix()
router.Get("/ *".func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
	// Perform processing
})
Copy the code