• 从源码对比DefaultServeMux 与 gorilla/mux


    从源码对比DefaultServeMux 与 gorilla/mux

    DefaultServeMux

    Golang自带的net/http库中包含了DefaultServeMux方法,以此可以搭建一个稳定的高并发的web server。

    DefaultServeMux源码分析

    • 路由表是实现路由功能的重要结构。muxEntry是存放具体的路由信息的一个map,key是路径,而value是该路径所对应的处理函数。

        type ServeMux struct {
                mu    sync.RWMutex
                m     map[string]muxEntry
        }
        type muxEntry struct {
                explicit bool
                h        Handler
                pattern  string
        }
      
    • 路由注册就是往map中插入数据,如果注册路径在当前路由表中不存在,则会在路由表中增加这一条路径数据,且处理函数是重定向到该路径,要注意的是注册路径要以/结尾才会添加到路由表中。

        func (mux *ServeMux) Handle(pattern string, handler Handler) {
                mux.mu.Lock()
                defer mux.mu.Unlock()
                mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
                n := len(pattern)
                if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
                        path := pattern
                        fmt.Printf("redirect for :%s to :%s", pattern, path)
                        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
                }
        }
      
    • 路由查找的过程实际上就是遍历路由表的过程,返回最长匹配请求路径的路由信息,找不到则返回NotFoundHandler。如果路径以xxx/结尾,则只要满足/xxx/* 就符合该路由。

        func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
                mux.mu.RLock()
                defer mux.mu.RUnlock()
                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) {
                var n = 0
                for k, v := range mux.m {
                        if !pathMatch(k, path) {
                        continue
                        }
                        //找出匹配度最长的
                        if h == nil || len(k) > n {
                        n = len(k)
                        h = v.h
                        pattern = v.pattern
                        }
                }
                return
        }
      
        func pathMatch(pattern, path string) bool {
        n := len(pattern)
                if pattern[n-1] != '/' {
                        return pattern == path
                }
                return len(path) >= n && path[0:n] == pattern
        }
      

    DefaultServeMux缺陷

    1. 不支持正则路由

    2. 只支持路径匹配,不支持按照Method,header,host等信息匹配,所以也就没法实现RESTful架构

    gorilla/mux

    • 路由信息存放在数组中,每一条路由信息都包括了路由的约束条件以及上层处理函数,当请求到来时,路由会遍历数组,找到第一个匹配的路由,并执行对应的处理函数,如果找不到则执行NotFoundHandler。

        type Router struct {
                routes []*Route
        }
        // Match matches registered routes against the request.
        func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
                for _, route := range r.routes {
                        //Route.Match会检查http.Request是否满足其设定的各种条件(路径,Header,Host..)
                        if route.Match(req, match) {
                        return true
                        }
                }
                return false
        }
        func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
                var match RouteMatch
                var handler http.Handler
                if r.Match(req, &match) {
                        handler = match.Handler
                }
                if handler == nil {
                        handler = http.NotFoundHandler()
                }
                handler.ServeHTTP(w, req)
        }
      
    • 当请求到来时,Route.Match()会遍历matcher数组,只有数组中所有的元素都返回true时则说明此请求满足该路由的限定条件。

        type Route struct {
                // Request handler for the route.
                handler http.Handler
                // List of matchers.
                matchers []matcher
        }
      
    • gorilla/mux使用了一个第三方模块gorilla/context。当http请求到来时,mux.Router会选择合适的路由,并提取出一些参数信息,将这些参数信息与http.Request对象在gorilla/context中建立映射关系,上层处理函数根据http.Request对象到context中找到该http.Request所对应的参数信息。

        var data  = make(map[*http.Request]map[interface{}]interface{})
        func Set(r *http.Request, key, val interface{}) {
        mutex.Lock()
                if data[r] == nil {
                        data[r] = make(map[interface{}]interface{})
                        datat[r] = time.Now().Unix()
                }
                data[r][key] = val
                mutex.Unlock()
        }
        func Get(r *http.Request, key interface{}) interface{} {
                mutex.RLock()
                if ctx := data[r]; ctx != nil {
                        value := ctx[key]
                        mutex.RUnlock()
                        return value
                }
                mutex.RUnlock()
                return nil
        }
      
    • 上层处理函数中调用mux.Vars(r)则可以取出该http.Request所关联的参数信息。val实际上是一个map[string][string],存放该请求对应的变量值集合。

        func setVars(r *http.Request, val interface{}) {
                context.Set(r, varsKey, val)
        }
        func Vars(r *http.Request) map[string]string {
                if rv := context.Get(r, varsKey); rv != nil {
                        //类型转换,如果失败直接panic
                        return rv.(map[string]string)
                }
                return nil
        }
  • 相关阅读:
    fastjson对象,JSON,字符串,map之间的互转
    bootstrap的页面刷新以及模态框的清空
    change 和 propertychange 事件监听input 并发起ajax请求
    jquery 获取和设置select的option值
    Mybatis中使用@Select注解进行模糊查询,使用concat关键字
    mysql 获取表字段及注释
    SpringBoot 在IDEA中实现热部署
    jquery与css控制元素的隐藏和显示的几种方法
    Java8 stream的详细用法
    FATAL ERROR: Could not find ./bin/my_print_defaults 解决方法
  • 原文地址:https://www.cnblogs.com/renleimlj/p/7787220.html
Copyright © 2020-2023  润新知