DefaultServeMux
与 gorilla/mux
对比阅读
Table of Contents
DefaultServeMux
的实现
查阅 net.http
的源码,可以得知 DefaultServeMux
的实现如下:DefaultServeMux
是库中 ServeMux
结构体的一个默认实例 defaultServeMux
的指针。
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
ServeMux
结构体的定义如下。其中,m
字段是一个 map
(路由表),实现了用户请求中 path
映射到 muxEntry
,muxEntry
中 h
字段存放了对应的 Handler
处理方法,从而实现了路径到处理方法的映射。
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
muxEntry
中的 Handler 是一个接口,其中包含了 ServeHTTP
用于处理 http 请求。调用 HandlerFunc
时可以给 DefaultServeMux
对特定的 pattern
增加对应的 handler
。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
路由器处理 http 请求时,会调用 match
函数匹配相应的处理方法。若可以成功匹配,返回对应的处理方法;否则,最长有效匹配的处理方法:
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
gorilla/mux
的实现
gorilla/mux
定义的路由器如下。其中,最主要的字段为 routes
切片,该切片存储了包括了路由的约束条件以及处理函数的路由信息:
type Router struct {
NotFoundHandler http.Handler
MethodNotAllowedHandler http.Handler
routes []*Route
namedRoutes map[string]*Route
KeepContext bool
middlewares []middleware
routeConf
}
type Route struct {
handler http.Handler
buildOnly bool
name string
err error
namedRoutes map[string]*Route
routeConf
}
当服务器接收到请求后,路由器会遍历路由表进行匹配。若匹配成功,函数返回真值;否则,判断默认的处理器 NotFoundHandler
是否为空。NotFoundHandler
不为空可以进行处理,返回 true
;否则,返回 false
。
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
for _, route := range r.routes {
if route.Match(req, match) {
if match.MatchErr == nil {
for i := len(r.middlewares) - 1; i >= 0; i-- {
match.Handler = r.middlewares[i].Middleware(match.Handler)
}
}
return true
}
}
if match.MatchErr == ErrMethodMismatch {
if r.MethodNotAllowedHandler != nil {
match.Handler = r.MethodNotAllowedHandler
return true
}
return false
}
if r.NotFoundHandler != nil {
match.Handler = r.NotFoundHandler
match.MatchErr = ErrNotFound
return true
}
match.MatchErr = ErrNotFound
return false
}
此外,gorilla/mux
实现了正则表达式的相关功能,其中一个示例如下:
func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
if r.err != nil {
return r.err
}
if typ == regexpTypePath || typ == regexpTypePrefix {
if len(tpl) > 0 && tpl[0] != '/' {
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
}
if r.regexp.path != nil {
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
}
}
rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
strictSlash: r.strictSlash,
useEncodedPath: r.useEncodedPath,
})
if err != nil {
return err
}
for _, q := range r.regexp.queries {
if err = uniqueVars(rr.varsN, q.varsN); err != nil {
return err
}
}
if typ == regexpTypeHost {
if r.regexp.path != nil {
if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
return err
}
}
r.regexp.host = rr
} else {
if r.regexp.host != nil {
if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
return err
}
}
if typ == regexpTypeQuery {
r.regexp.queries = append(r.regexp.queries, rr)
} else {
r.regexp.path = rr
}
}
r.addMatcher(rr)
return nil
}
DefaultServeMux
与 gorilla/mux
的比较
DefaultServeMux:
- 不支持正则表达式进行路由,只支持路径匹配
- 路由过程使用顺序遍历的方式进行匹配,时间复杂度较高
gorilla/mux:
- 实现了
http.Handler
接口,与标准库的http.ServeMux
兼容 - 请求可以基于 URL 主机、路径、路径前缀、scheme、请求头、请求参数、请求方法或其他匹配方法进行路由匹配
- URL 主机、路径、查询字符串支持可选的正则匹配
- 支持构建或反转已注册的 URL 主机,以便维护对资源的引用
- 支持路由嵌套,以便不同路由可以共享通用条件,比如主机、路径前缀或其他重复的属性。通过这个功能可以优化请求的匹配速率