erouter是高性能高擴展http路由庫,具備零內存複製、嚴格路由匹配順序、代碼複製度低、組路由、中間件功能、默認參數、常量匹配、變量匹配、通配符匹配、變量校驗匹配、通配符校驗匹配、基於Host路由這些特色功能。linux
設計說明git
基於eudore框架路由分離,修改中間件機制並移除MVC。github
RouterRadix使用基數樹實現,具備零內存複製、嚴格路由匹配順序、組路由、中間件功能、默認參數、常量匹配、變量匹配、通配符匹配功能。golang
example:web
package main
import "log"
import "net/http"
import "github.com/eudore/erouter"
func main() {
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)
}
複製代碼
測試命令:正則表達式
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/getuser
複製代碼
RouterFull基於RouterRadix擴展,實現變量校驗匹配、通配符校驗匹配功能。api
用法:在正常變量和通配符後,使用'|'符號分割,後爲校驗規則,isnum是校驗函數;min:100爲動態檢驗函數,min是動態校驗函數名稱,':'後爲參數;若是爲'^'開頭爲正則校驗,而且要使用'$'做爲結尾。bash
注意: 正則表達式不要使用空格,會致使參數切割錯誤,使用\u002代替空格。框架
:num|isnum
:num|min:100
:num|^0.*$
*num|isnum
*num|min:100
*num|^0.*$
複製代碼
example:curl
package main
import "net/http"
import "github.com/eudore/erouter"
func main() {
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)
}
複製代碼
測試命令:
curl 127.0.0.1:8080/get
curl 127.0.0.1:8080/012
curl 127.0.0.1:8080/123
curl 127.0.0.1:8080/12
curl 127.0.0.1:8080/Erouter/123
複製代碼
RouterHost基於Host匹配,經過選擇Host對應子路由器執行註冊和匹配,實現基於Host路由功能。
當前使用遍歷匹配,Host匹配函數爲path.Match,將來匹配規則僅保留'*'通配符和常量。
用法:須要給Host路由器註冊域名規則下的子路由器,註冊路由時使用host參數匹配註冊的路由器添加路由,匹配時使用請求的Host來匹配註冊的路由。
example:
package main
import "net/http"
import "github.com/eudore/erouter"
func main() {
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)
}
複製代碼
測試命令:
curl 127.0.0.1:8080
curl -XPUT 127.0.0.1:8080
curl -H 'Host: www.example.com' 127.0.0.1:8080
curl -H 'Host: www.example.com' -XPUT 127.0.0.1:8080
curl -H 'Host: www.example.com' -Xput 127.0.0.1:8080
複製代碼
使用GithubApi進行Benchmark性能測試,Erouter匹配性能僅有httprouter的60%,可是具備嚴格路由匹配順序、易擴展重寫和代碼複雜度低的特色。
測試命令:
go get github.com/eudore/web-framework-benchmark
go test -bench=router github.com/eudore/web-framework-benchmark
複製代碼
測試結果:
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.934s
複製代碼
列出了主要使用的方法,具體參考文檔。
Router接口定義:
type (
// Params讀寫請求處理中的參數。
Params interface {
GetParam(string) string
AddParam(string, string)
SetParam(string, string)
}
// Erouter處理一個請求的方法,在http.HandlerFunc基礎上增長了Parmas。
Handler func(http.ResponseWriter, *http.Request, Params) // 定義請求處理中間件函數,經過傳入處理而後返回一個處理,使用裝飾器組裝處理請求。 Middleware func(Handler) Handler // The route is directly registered by default. Other methods can be directly registered using the RouterRegister interface. // // 路由默認直接註冊的方法,其餘方法能夠使用RouterRegister接口直接註冊。 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.
//
// 路由器核心接口,執行路由、中間件的註冊和處理http請求。
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.
//
// 路由器接口,須要實現路由器方法、路由器核心兩個接口。
Router interface {
RouterCore
RouterMethod
}
)
複製代碼
當前擁有三種實現,每種路由器都實現了Router接口。
func NewRouterRadix() Router
func NewRouterFull() Router
func NewRouterHost() Router
複製代碼
router1 := erouter.NewRouterRadix()
router2 := erouter.NewRouterFull()
router3 := erouter.NewRouterHost()
複製代碼
func Group(path string) RouterMethod
Group實現路由器分組。
router := erouter.NewRouterRadix()
apiv1 := router.Group("/api/v1 version=v1")
apiv1.Get("/*", ...)
複製代碼
func AddHandler(method string, path string, handler Handler) RouterMethod
AddHandler用於添加新路由。
router := erouter.NewRouterRadix()
router.AddHandle("GET", "/*", ...)
複製代碼
func AddMiddleware(method string, path string, midds ...Middleware) RouterMethod
AddMiddleware給當前路由方法添加處理中間件。
router := erouter.NewRouterRadix()
router.AddMiddleware("ANY", "", func(h erouter.Handler) erouter.Handler {
return func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
// befor執行
...
// 調用next處理彙總函數
h(w, r, p)
// after執行
...
}
})
複製代碼
func NotFound(Handler)
設置路由器404處理。
func MethodNotAllowed(Handler)
設置路由器405處理。
func Any(path string, handler Handler)
註冊Any方法,至關於AddHandler的方法爲"ANY"。
Any方法的集合爲erouter.RouterAllMethod,擴展新方法Radix和Full不支持。
func Get(path string, handler Handler)
註冊Get方法,至關於AddHandler的方法爲"GET",post、put等方法函數相似。
router := erouter.NewRouterRadix()
router.Get("/*", func(w http.ResponseWriter, r *http.Request, p erouter.Params) {
// 執行處理
})
複製代碼