mux库源码分析

mux源码分析

 mux 是go实现的轻量的路由,可以基于host,path,query匹配

  • 源码地址

  • 官方提供的使用实例

r := mux.NewRouter()
r.HandleFunc("/products/{key}", ProductHandler)
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
  • Router类型结构

 实现了http.handler接口,定义了根路由,可以通过HandleFunc注册多个path 和handler,还有找到匹配项之后调用的中间片

type Router struct {
	// Configurable Handler to be used when no route matches.
	NotFoundHandler http.Handler
	// Configurable Handler to be used when the request method does not match the route.
	MethodNotAllowedHandler http.Handler
	// Routes to be matched, in order.
	routes []*Route
	// Routes by name for URL building.
	namedRoutes map[string]*Route
	// If true, do not clear the request context after handling the request.
	//
	// Deprecated: No effect, since the context is stored on the request itself.
	KeepContext bool
	// Slice of middlewares to be called after a match is found
	middlewares []middleware
	// configuration shared with `Route`
	routeConf
}

  • Routeconf类型结构

 Routeconf是“路由器”和“路由”之间共享的公共路由配置,内部结构中 [ ]matcher 用于存储多个匹配条件

 routeRegexpGroup 用于保存匹配成功后提取的参数的实际值。http请求匹配成功后,需要将参数传递到handler处理器中,参数可以从此结构获取

type routeConf struct {
	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
	useEncodedPath bool

	// If true, when the path pattern is "/path/", accessing "/path" will
	// redirect to the former and vice versa.
	strictSlash bool

	// If true, when the path pattern is "/path//to", accessing "/path//to"
	// will not redirect
	skipClean bool

	// Manager for the variables from host and path.
	regexp routeRegexpGroup

	// List of matchers.
	matchers []matcher

	// The scheme used when building URLs.
	buildScheme string

	buildVarsFunc BuildVarsFunc
}
  • routeRegexp 类型结构
type routeRegexp struct {
    // The unmodified template.
    template string
    // The type of match
    regexpType regexpType
    // Options for matching
    options routeRegexpOptions
    // Expanded regexp.
    regexp *regexp.Regexp
    // Reverse template.
    reverse string
    // Variable names.
    varsN []string
    // Variable regexps (validators).
    varsR []*regexp.Regexp
}

原理:
addRegexpMatcher方法是Route对象的比较核心的部分,r.getRegexpGroup()方法和继承父路由中的routeRegexpGroup或者新建一个空的routeRegexpGroup。接下来是调用newRouteRegexp方法,根据request生成一个routeRegexp,最后再把生成的routeRegexp追加到Route的matchers中,所以我们现在可以知道Route中的matchers对应的是一个routeRegexp

func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
    if r.err != nil {
        return r.err
    }
    r.regexp = r.getRegexpGroup()
    if !matchHost && !matchQuery {
        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, matchHost, matchPrefix, matchQuery, r.strictSlash, 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 matchHost {
        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 matchQuery {
            r.regexp.queries = append(r.regexp.queries, rr)
        } else {
            r.regexp.path = rr
        }
    }
    r.addMatcher(rr)
    return nil
}

matcher接口:对于不同的匹配类型抽象出来的不同接口

  • Method方法匹配:

 添加一个methodMatcherroutematcher切片中。 methodMatcher的Match方法实现为:取request中的方法进行匹配。

// Methods --------------------------------------------------------------------
// methodMatcher matches the request against HTTP methods.
type methodMatcher []string

func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
    return matchInArray(m, r.Method)
}

// Methods adds a matcher for HTTP methods.
// It accepts a sequence of one or more methods to be matched, e.g.:
// "GET", "POST", "PUT".
func (r *Route) Methods(methods ...string) *Route {
    for k, v := range methods {
        methods[k] = strings.ToUpper(v)
    }
    return r.addMatcher(methodMatcher(methods))
}
  • Path匹配:

 简单的调用addRegexpMatcher方法,给Route增加一个Match

func (r *Route) Path(tpl string) *Route {
    r.err = r.addRegexpMatcher(tpl, regexpTypePath)
    return r
}
  • Route中HandlerFunc的作用

HandlerFunc是Route对象的方法,可以给一条Route注册一个回调函数。在r.Handler(http.HandlerFunc(f))中, 再次调用了Route的Handler函数

func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
    return r.Handler(http.HandlerFunc(f))
}

ListenAndServe启动服务

  • serveHTTP的实现:

 其中skipCleanuseEncodedPath默认为false。核心部分是从 r.Match(req, &match)开始的,Match方法定义如下,首先会遍历Router中的所有路由route的Match方法,如有匹配到,则直接返回,否则返回NotFoundHandler

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {

if !r.skipClean {
    path := req.URL.Path
    if r.useEncodedPath {
        path = getPath(req)
    }
    // Clean path to canonical form and redirect.
    if p := cleanPath(path); p != path {

        // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
        // This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
        // http://code.google.com/p/go/issues/detail?id=5252
        url := *req.URL
        url.Path = p
        p = url.String()

        w.Header().Set("Location", p)
        w.WriteHeader(http.StatusMovedPermanently)
        return
    }
}
var match RouteMatch
var handler http.Handler
if r.Match(req, &match) {
    handler = match.Handler
    req = setVars(req, match.Vars)
    req = setCurrentRoute(req, match.Route)
}
if handler == nil {
    handler = http.NotFoundHandler()
}
if !r.KeepContext {
    defer contextClear(req)
}
handler.ServeHTTP(w, req)
}

寻找一个简单的样例,来简单理解mux实现原理

  • 测试结果:
    mux库源码分析_第1张图片

  • 访问地址通过回调函数HandleFunc获取对应的URL:
    mux库源码分析_第2张图片

参考资料

Golang 第三方库学习 · mux
mux源码地址


基本任务:cloudgo–github传送门

你可能感兴趣的:(服务计算作业)