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

1 回复

更多关于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)
})

性能优化技巧

  1. 减少中间件数量:每个中间件都会增加处理时间
  2. 避免在路由处理中进行复杂计算:将耗时操作异步处理
  3. 使用路由分组:提高路由匹配效率
  4. 合理使用缓存:对频繁访问的路由结果进行缓存

完整示例

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 等框架。

回到顶部