本文主要是介绍GO语言web开发的几种Mux库阅读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
DefaultServeMux阅读
可以查看到ServeMux结构的定义如下:
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryhosts bool
}type muxEntry struct {explicit boolh Handlerpattern string
}
ServeMux结构中最重要的字段为map[string]muxEntry类型的m,这是一个map,key为表示url模式的string,value则是muxEntry结构。可以看到muxEntry结构中存储了表示具体的url模式的string和handler,以及是否explicit的bool类型。由于ServeMux也实现了ServeHTTP函数,其也可以看成是一个handler接口,与之前ServeHTTP函数处理http请求并产生http响应不同,ServeMux的ServeHTTP方法是用来找到路由对应的handler的。
ServeMux的任务是将用户请求中 path 映射到 Handler,由于其可能会被并发访问,因此其还有一个子段mu作为sync.RWMutex类型,也就是读写互斥锁,其保证不同go程对该线程不安全变量的互斥访问。
可以查看到ServeMux的Match函数如下:
// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {// Check for exact match first.v, ok := mux.m[path]if ok {return v.h, v.pattern}// Check for longest valid match. mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, ""
}
可以看到,其首先检查mux.m中是否含有path对应的精确匹配,如果有的话就直接返回对应的handler和pattern。否则则遍历所有的patterns,由于patterns首先按照长度从大到小排序,因此其会找到具有最长匹配前缀的pattern并将其和对应的handler返回。这相比过去版本的match有了优化,可以更快地找到第一个具有最长匹配的pattern。
而Handle函数可以用于注册路由,可以看到其实现如下:
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {mux.mu.Lock()defer mux.mu.Unlock()if pattern == "" {panic("http: invalid pattern")}if handler == nil {panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {panic("http: multiple registrations for " + pattern)}if mux.m == nil {mux.m = make(map[string]muxEntry)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}
}
正如之前所述,由于ServeMux不是线程安全的,因此Handle函数首先将ServerMux加上互斥锁,并在函数处理完后解开互斥锁。之后其检查pattern和handler是否是合法且是没有注册过的,如果是的话则将其加入到mux.m这个map中(如果还没有申请mux.m就创建)。之后按照长度的顺序将其插入到es中,使得插入后es仍然有序。
然后可以查看到handler方法:
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()// Host-specific pattern takes precedence over generic onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return
}
同样,其首先上互斥锁,然后调用match检查是否存在host+path路径,如果有就返回对应的h和pattern,否则再调用match检查是否存在path路径,如果有也返回对应的h和pattern。否则就进入NotFoundHandler的处理。
// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
NotFoundHandler如上吗,将NotFound作为参数,使用HandlerFunc函数处理,也就是处理了404错误的情况。
经过上面的分析,可以看到DefaultServeMux实现了对用户请求url进行路由的功能。可以看到,DefaultServeMux支持url的精确匹配和最长前缀匹配。但由于路由的时候还需要一条条检查所有项目来匹配,因此在路由条目比较多的时候可能还是比较慢。为了加快速度,可以像其他一些后端路由一样采取基于Trie前缀树(用/分隔)等层次匹配的算法,可以使得最长查找次数为树的高度。此外,DefaultServeMux也不支持Method,header,host等信息匹配以及正则表达式的url匹配。
gorilla/mux阅读
进一步对比阅读gorilla/mux代码。可以看到gorilla/mux比DefaultServeMux实现了更强大的功能,包括正则表达式匹配以及Method,header,host等信息的匹配,利用gorilla/mux可以更方便地开发server程序。
可以看到其定义Route如下,Route结构存储了匹配请求的handler和name等结构其还有着map[string]类型指针,指向全局索引所有命名Routes的结构。
// Route stores information to match a request and build URLs.
type Route struct {// Request handler for the route.handler http.Handler// If true, this route never matches: it is only used to build URLs.buildOnly bool// The name used to build URLs.name string// Error resulted from building a route.err error// "global" reference to all named routesnamedRoutes map[string]*Route// config possibly passed in from `Router`routeConf
}
Router的代码则如下:
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 foundmiddlewares []middleware// configuration shared with `Route`routeConf
}
可以看到,其包括特定的NotFoundHandler 和MethodNotAllowedHandler 字段来处理页面没有找到,和方法不允许的通常情况,这两个handler也是http.handler类型。而其存储路由条目则是用一个[]*Route存储,同时还有一个map[string]*Route类型根据名称索引到对应路由。
其Match函数与net/http包中的不同,其首先遍历所有routes,然后调用route.Match看能否匹配,如果可以匹配就检查如果在没有错误的情况下,添加各个中间件作为回调函数链。这样可以支持服务端通过中间件实现更强大的路由功能。之后其检查方法如果不匹配的话,就用MethodNotAllowedHandler进行处理,最后检查如果是没有找到页面的话,就用NotFoundHandler 进行处理。
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {for _, route := range r.routes {if route.Match(req, match) {// Build middleware chain if no error was foundif 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.MethodNotAllowedHandlerreturn true}return false}// Closest match for a router (includes sub-routers)if r.NotFoundHandler != nil {match.Handler = r.NotFoundHandlermatch.MatchErr = ErrNotFoundreturn true}match.MatchErr = ErrNotFoundreturn false
}
进一步查看route.Match函数的代码如下:
// Match matches the route against the request.
func (r *Route) Match(req *http.Request, match *RouteMatch) bool {if r.buildOnly || r.err != nil {return false}var matchErr error// Match everything.for _, m := range r.matchers {if matched := m.Match(req, match); !matched {if _, ok := m.(methodMatcher); ok {matchErr = ErrMethodMismatchcontinue}// Ignore ErrNotFound errors. These errors arise from match call// to Subrouters.//// This prevents subsequent matching subrouters from failing to// run middleware. If not ignored, the middleware would see a// non-nil MatchErr and be skipped, even when there was a// matching route.if match.MatchErr == ErrNotFound {match.MatchErr = nil}matchErr = nilreturn false}}if matchErr != nil {match.MatchErr = matchErrreturn false}if match.MatchErr == ErrMethodMismatch && r.handler != nil {// We found a route which matches request method, clear MatchErrmatch.MatchErr = nil// Then override the mis-matched handlermatch.Handler = r.handler}// Yay, we have a match. Let's collect some info about it.if match.Route == nil {match.Route = r}if match.Handler == nil {match.Handler = r.handler}if match.Vars == nil {match.Vars = make(map[string]string)}// Set variables.r.regexp.setMatch(req, match, r)return true
}
可以看到,其首先检查buildOnly和err,在buildOnly为假和err为nil的情况下,遍历r.matchers ,调用m.Match检查是否方法匹配,如果发生错误就进行错误处理,直到找到匹配的match。之后就收集相关信息,设定对应的Route和handler,以及参数Vars,之后通过正则表达式的setMatch匹配相应正则表达式中的参数。
然后可以查看Vars函数如下,其取出http请求相关联的变量的信息。该函数从上下文中获得对应的varsKey,将存放该请求对应的变量值集合的string映射到string的map类型返回。
func Vars(r *http.Request) map[string]string {if rv := r.Context().Value(varsKey); rv != nil {return rv.(map[string]string)}return nil
}
还可以查看handler的实现如下,HandlerFunc调用Route的Handler函数字段对http.HandlerFunc(f)返回的带handler的Route进行处理。而Handler函数则设置了Route的handler并将其返回。
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {return r.Handler(http.HandlerFunc(f))
}
// Handler sets a handler for the route.
func (r *Route) Handler(handler http.Handler) *Route {if r.err == nil {r.handler = handler}return r
}
查看Router的ServeHTTP函数如下,其处理http的请求,并在http.ResponseWriter类型参数中写入响应报文。其首先在r.skipClean为假的情况下获得请求的url路径,之后在r.useEncodedPath为真的情况下调用EscapedPath()对路径进行处理。之后其清理路径到规范的形式并重定向。之后就检查是否有match和请求相匹配,匹配的话就设定handler为match的Handler字段,以及设置请求包括match的参数,路由等。之后看是否需要用到methodNotAllowedHandler和NotFoundHandler,最终让handler处理请求。
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {if !r.skipClean {path := req.URL.Pathif r.useEncodedPath {path = req.URL.EscapedPath()}// 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=5252url := *req.URLurl.Path = pp = url.String()w.Header().Set("Location", p)w.WriteHeader(http.StatusMovedPermanently)return}}var match RouteMatchvar handler http.Handlerif r.Match(req, &match) {handler = match.Handlerreq = requestWithVars(req, match.Vars)req = requestWithRoute(req, match.Route)}if handler == nil && match.MatchErr == ErrMethodMismatch {handler = methodNotAllowedHandler()}if handler == nil {handler = http.NotFoundHandler()}handler.ServeHTTP(w, req)
}
这篇关于GO语言web开发的几种Mux库阅读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!