一、执行流程

构建一个简单http server:

package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello world"))
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}
http://127.0.0.1:8080/

通过跟踪http.go包代码,可以发现执行流程基本如下:

Listener8080
for

3.接收到请求,并创建一个conn对象,放入goroutine处理(实现高并发关键)

4.解析请求来源信息获得请求路径等重要信息

5.请求ServerHTTP方法,已经通过上一步获得了ResponseWriter和Request对象

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    //此handler即为http.ListenAndServe 中的第二个参数
    handler := sh.srv.Handler 
    if handler == nil {
        //如果handler为空则使用内部的DefaultServeMux 进行处理
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    //这里就开始处理http请求
    //如果需要使用自定义的mux,就需要实现ServeHTTP方法,即实现Handler接口。
    handler.ServeHTTP(rw, req)
}

6.进入DefaultServeMux中的逻辑就是根据请求path在map中匹配查找handler,并交由handler处理

二、DefaultServeMux 路由匹配规则

先看几个路由规则:

package main

import (
    "log"
    "net/http"
)

func main() {
    //规则1
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello world"))
    })

    //规则2
    http.HandleFunc("/path/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("pattern path: /path/ "))
    })

    //规则3
    http.HandleFunc("/path/subpath", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("pattern path: /path/subpath"))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

情景一:

http://127.0.0.1:8080/
hello world

情景二:

http://127.0.0.1:8080/path
pattern path: /path/

情景三:

http://127.0.0.1:8080/path/subpath/
pattern path: /path/

情景四:

http://127.0.0.1:8080/hahaha/
hello world

先说明一些规则吧,再看代码是怎么实现的:

//path//path/

2.我设置了这么多规则为什么规则一可以通用匹配未设置的路由信息,而且又不影响已经存在路由, 内部是怎么实现的?

2.1 添加路由规则

先看两个struct,这是存放默认路由规则的:

type ServeMux struct {
    mu    sync.RWMutex  //处理并发,增加读写锁
    m     map[string]muxEntry  //存放规则map,key即为设置的path
    hosts bool // whether any patterns contain hostnames(是否包含host)
}

type muxEntry struct {
    explicit bool //是否完全匹配
    h        Handler//相应匹配规则的handler
    pattern  string//匹配路径
}
http.HandleFuncstruct
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    //如果已经匹配到了则panic
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    //增加一个新的匹配规则
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    //根据path的第一个字母判断是否有host
    if pattern[0] != '/' {
        mux.hosts = true
    }

    //!!这里看清楚 就是实现了情景二的情况 ,看判断条件
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit{
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern
        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}
Helpful behavior//path/

2.2 查找路由规则

ServeMux/path/subpath/path/subpath/path/
mux.match
mux.ServerHTTPmux.Handlermux.handlermux.match
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    var n = 0
    for k, v := range mux.m {
        if !pathMatch(k, path) {
            continue
        }
        //如果匹配到了一个规则,并没有马上返回handler,而且继续匹配并且判断path的长度是否是最长的,这是关键!!!
        if h == nil || len(k) > n {
            n = len(k)
            h = v.h
            pattern = v.pattern
        }
    }
    return
}
/pathMatch/
h.ServeHTTP(w, r)
ServeHTTP
http.HandleFuncHandlerFuncServeHTTP
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
f(w,r)