golang高性能HTTP路由插件库xujiajun/gorouter的使用
golang高性能HTTP路由插件库xujiajun/gorouter的使用
xujiajun/gorouter是一个简单快速的Go HTTP路由库,易于构建RESTful API和Web框架。
动机
作者需要一个支持正则表达式的高性能Go HTTP路由器。虽然julienschmidt/httprouter非常快但不支持正则,而gorilla/mux功能强大但性能较慢。因此作者开发了xujiajun/gorouter,可能是目前支持正则表达式的最快Go HTTP路由器。
特性
- 高性能
- URL参数支持
- 正则表达式参数支持
- 路由分组
- 反向路由
- 自定义NotFoundHandler
- 自定义PanicHandler
- 中间件链支持
- 静态文件服务
- 熟悉的模式规则
- 支持HTTP方法:Get、Post、Delete、Put、Patch
- 无外部依赖(仅Go标准库)
要求
- Go 1.8+
安装
go get -u github.com/xujiajun/gorouter
使用示例
静态路由
package main
import (
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
URL参数
package main
import (
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
//url参数匹配
mux.GET("/user/:id", func(w http.ResponseWriter, r *http.Request) {
//获取单个URL参数
id := gorouter.GetParam(r, "id")
//获取所有URL参数
//id := gorouter.GetAllParams(r)
//fmt.Println(id)
w.Write([]byte("match user/:id ! get id:" + id))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
正则参数
package main
import (
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
//url正则匹配
mux.GET("/user/{id:[0-9]+}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("match user/{id:[0-9]+} !"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
路由分组
package main
import (
"fmt"
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func usersHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "/api/users")
}
func main() {
mux := gorouter.New()
mux.Group("/api").GET("/users", usersHandler)
log.Fatal(http.ListenAndServe(":8181", mux))
}
反向路由
package main
import (
"fmt"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
routeName1 := "user_event"
mux.GETAndName("/users/:user/events", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/users/:user/events"))
}, routeName1)
routeName2 := "repos_owner"
mux.GETAndName("/repos/{owner:\\w+}/{repo:\\w+}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/repos/{owner:\\w+}/{repo:\\w+}"))
}, routeName2)
params := make(map[string]string)
params["user"] = "xujiajun"
fmt.Println(mux.Generate(http.MethodGet, routeName1, params)) // /users/xujiajun/events <nil>
params = make(map[string]string)
params["owner"] = "xujiajun"
params["repo"] = "xujiajun_repo"
fmt.Println(mux.Generate(http.MethodGet, routeName2, params)) // /repos/xujiajun/xujiajun_repo <nil>
}
自定义NotFoundHandler
package main
import (
"fmt"
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func notFoundFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404 page !!!")
}
func main() {
mux := gorouter.New()
mux.NotFoundFunc(notFoundFunc)
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
自定义PanicHandler
package main
import (
"fmt"
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
mux.PanicHandler = func(w http.ResponseWriter, req *http.Request, err interface{}) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println("err from recover is :", err)
fmt.Fprint(w, "received a panic")
}
mux.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("panic")
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
中间件链
package main
import (
"fmt"
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
type statusRecorder struct {
http.ResponseWriter
status int
}
func (rec *statusRecorder) WriteHeader(code int) {
rec.status = code
rec.ResponseWriter.WriteHeader(code)
}
//https://upgear.io/blog/golang-tip-wrapping-http-response-writer-for-middleware/
func withStatusRecord(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rec := statusRecorder{w, http.StatusOK}
next.ServeHTTP(&rec, r)
log.Printf("response status: %v\n", rec.status)
}
}
func notFoundFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Not found page !")
}
func withLogging(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Logged connection from %s", r.RemoteAddr)
next.ServeHTTP(w, r)
}
}
func withTracing(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Tracing request for %s", r.RequestURI)
next.ServeHTTP(w, r)
}
}
func main() {
mux := gorouter.New()
mux.NotFoundFunc(notFoundFunc)
mux.Use(withLogging, withTracing, withStatusRecord)
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
静态文件服务
package main
import (
"log"
"net/http"
"os"
"github.com/xujiajun/gorouter"
)
//ServeFiles serve static resources
func ServeFiles(w http.ResponseWriter, r *http.Request) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
dir := wd + "/examples/serveStaticFiles/files"
http.StripPrefix("/files/", http.FileServer(http.Dir(dir))).ServeHTTP(w, r)
}
func main() {
mux := gorouter.New()
mux.GET("/hi", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
})
//defined prefix
mux2 := mux.Group("/files")
//http://127.0.0.1:8181/files/demo.txt
//will match
mux2.GET("/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
ServeFiles(w, r)
})
//http://127.0.0.1:8181/files/a/demo2.txt
//http://127.0.0.1:8181/files/a/demo.txt
//will match
mux2.GET("/{fileDir:[0-9a-zA-Z_.]+}/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
ServeFiles(w, r)
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
模式规则
语法 | 描述 | 示例 |
---|---|---|
:name |
命名参数 | /user/:name |
{name:regexp} |
带正则的命名参数 | /user/{name:[0-9a-zA-Z]+} |
:id |
带正则的命名参数 | /user/:id |
:id
是{id:[0-9]+}
的简写,:name
是{name:[0-9a-zA-Z_]+}
的简写
性能基准
基准测试结果显示xujiajun/gorouter在支持正则的路由器中性能表现优异。
结论
- 性能(xujiajun/gorouter、julienschmidt/httprouter和teambition/trie-mux都很快)
- 内存消耗(xujiajun/gorouter和julienschmidt/httprouter较少)
- 特性(julienschmidt/httprouter不支持正则,其他支持)
如果需要支持正则的高性能路由器,xujiajun/gorouter是不错的选择。
如果不需要支持正则的高性能路由器,julienschmidt/httprouter是不错的选择。
最终,正如julienschmidt所说:“性能不能是选择路由器的唯一标准。尝试一些路由器,选择你最喜欢的一个。”
贡献
欢迎提交Pull Request。
作者
xujiajun
许可证
MIT许可
致谢
该项目受到以下项目的启发:
- httprouter
- bone
- trie-mux
- alien
更多关于golang高性能HTTP路由插件库xujiajun/gorouter的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高性能HTTP路由插件库xujiajun/gorouter的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
xujiajun/gorouter 高性能HTTP路由库使用指南
xujiajun/gorouter 是一个轻量级、高性能的Go语言HTTP路由器,它简单易用且性能优异,适合构建RESTful API服务。
主要特性
- 支持静态路由和参数路由
- 高性能的路由匹配
- 支持中间件
- 简单直观的API设计
- 轻量级,无外部依赖
安装
go get github.com/xujiajun/gorouter
基本使用
1. 创建路由实例
package main
import (
"fmt"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World!")
})
mux.GET("/hello/:name", func(w http.ResponseWriter, r *http.Request) {
name := gorouter.GetParam(r, "name")
fmt.Fprintf(w, "Hello %s!", name)
})
http.ListenAndServe(":8080", mux)
}
2. 路由参数
mux.GET("/user/:id", func(w http.ResponseWriter, r *http.Request) {
id := gorouter.GetParam(r, "id")
fmt.Fprintf(w, "User ID: %s", id)
})
mux.GET("/post/:year/:month/:day", func(w http.ResponseWriter, r *http.Request) {
year := gorouter.GetParam(r, "year")
month := gorouter.GetParam(r, "month")
day := gorouter.GetParam(r, "day")
fmt.Fprintf(w, "Date: %s-%s-%s", year, month, day)
})
3. 支持所有HTTP方法
mux.GET("/get", handler)
mux.POST("/post", handler)
mux.PUT("/put", handler)
mux.DELETE("/delete", handler)
mux.PATCH("/patch", handler)
mux.OPTIONS("/options", handler)
mux.HEAD("/head", handler)
4. 中间件支持
func Logger(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s %s\n", r.Method, r.URL.Path)
next(w, r)
}
}
func Auth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token != "valid-token" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
mux.GET("/secure", Logger(Auth(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Secure Content")
})))
5. 分组路由
api := mux.Group("/api")
api.GET("/users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "User List")
})
v1 := api.Group("/v1")
v1.GET("/products", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Products v1")
})
6. 获取查询参数
mux.GET("/search", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("q")
fmt.Fprintf(w, "Search query: %s", query)
})
性能优化技巧
- 减少中间件数量:每个中间件都会增加处理时间
- 避免在路由处理中进行复杂计算:将耗时操作异步处理
- 使用路由分组:提高路由匹配效率
- 合理使用缓存:对频繁访问的路由结果进行缓存
完整示例
package main
import (
"fmt"
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
// 全局中间件
mux.Use(LoggerMiddleware)
// 首页
mux.GET("/", HomeHandler)
// 用户路由组
user := mux.Group("/user")
user.GET("/:id", GetUserHandler)
user.POST("/", CreateUserHandler)
user.PUT("/:id", UpdateUserHandler)
user.DELETE("/:id", DeleteUserHandler)
// 需要认证的API
auth := mux.Group("/secure")
auth.Use(AuthMiddleware)
auth.GET("/data", SecureDataHandler)
log.Println("Server started on :8080")
http.ListenAndServe(":8080", mux)
}
func LoggerMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next(w, r)
}
}
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-API-Key") != "secret" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome to the Home Page!")
}
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
id := gorouter.GetParam(r, "id")
fmt.Fprintf(w, "Get User ID: %s", id)
}
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "User Created")
}
func UpdateUserHandler(w http.ResponseWriter, r *http.Request) {
id := gorouter.GetParam(r, "id")
fmt.Fprintf(w, "User ID %s Updated", id)
}
func DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
id := gorouter.GetParam(r, "id")
fmt.Fprintf(w, "User ID %s Deleted", id)
}
func SecureDataHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "This is secure data")
}
xujiajun/gorouter 是一个简单高效的路由库,适合中小型项目使用。对于更复杂的路由需求,可以考虑 gorilla/mux 或 gin 等框架。