DefaultServeMux 与 gorilla/mux 对比阅读

DefaultServeMux 与 gorilla/mux 对比阅读

DefaultServeMux 实现

查阅 net.http 源码,可以找到 DefaultServeMux 的实现:指向 ServeMux 默认实例 defaultServeMux

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

结构体 ServeMux 的定义如下;其中用于路由的字段有 mes ;字段 m 是映射 map[string]muxEntry ,将用户请求中的 path 映射到 muxEntry ,用于完全匹配;字段 esmuxEntry 由长到短排列的切片,用于最长有效匹配;

muxEntry 定义如下;字段 h 用于请求处理的,是所匹配路径对应的 Handle 处理方法;字段 pattern 指明可以匹配的模式,适用于前缀匹配;

// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
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
}

Handle 定义如下;是包含 ServeHTTP 的接口;在函数 ServeHTTP 中将会对请求进行具体处理;

// A Handler responds to an HTTP request.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

HandleFunc 可以给 DefaultServeMux 对特定的 pattern 增加对应的 handler ;首先用输入的 patternhandler 生成新的 muxEntry ,然后在映射 m 中增加该 pattern 到新的 muxEntry 的映射,并按照由长到短的顺序放入 es 中;

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

match 实现路径匹配;先是在映射 m 中查找请求的路径 path 有无相应的 muxEntry ,有则说明 path 和该 muxEntrypattern 完全匹配,返回;然后在切片 es 中按照前缀匹配,由长到短寻找 pattern ,有则说明 path 和该 muxEntrypattern 前缀匹配,返回;最后匹配失败,返回空;

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 定义的路由器 Router 如下;其中用于路由匹配的主要字段是 routes*Route 切片,有序排列,类似于 ServeMux 中的字段 es

Route 定义如下;字段 handler 用于 http 请求处理;注意到还有字段 namedRoutes ,是映射 map[string]*Route ,用于实现嵌套路由;

type Router struct {
    NotFoundHandler http.Handler
    MethodNotAllowedHandler http.Handler
    // Routes to be matched, in order.
    routes []*Route
    // Routes by name for URL building.
    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
}

Match 实现路径匹配;先遍历 routes 进行匹配,匹配成功则返回 true ;然后检查默认处理函数 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主机,路径,路径前缀,方案,标头和查询值,HTTP方法或使用自定义匹配器来匹配请求
  • URL主机,路径和查询值可以具有带可选正则表达式的变量
  • 可以构建或“反转”已注册的URL,这有助于维护对资源的引用
  • 路由可用作子路由:仅在父路由匹配时才测试嵌套路由;对于具有共同条件(例如主机,路径前缀或其他重复属性)的路由组,可以优化请求匹配
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值