Nethttp Gin

net/http 路由注册

1func test1() {
2    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
3        fmt.Fprintf(w, "Hello world!")
4    })
5    err := http.ListenAndServe(":9001", nil)
6    if err != nil {
7        log.Fatal("ListenAndServer:", err)
8    }
9}

在使用ListenAndServe这个方法时,系统就会给我们指派一个路由器,DefaultServeMux是系统默认使用的路由器,如果ListenAndServe这个方法的第2个参数传入nil,系统就会默认使用DefaultServeMux。当然,这里也可以传入自定义的路由器。

先看http.HandleFunc("/", ...),从HandleFunc方法点进去,如下:

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

在这里调用了DefaultServeMuxHandleFunc方法,这个方法有两个参数,pattern是匹配的路由规则,handler表示这个路由规则对应的处理方法,并且这个处理方法有两个参数。

在我们书写的代码示例中,pattern对应/handler对应sayHello,当我们在浏览器中输入http://localhost:9001时,就会触发匿名函数。

我们再顺着DefaultServeMuxHandleFunc方法继续点下去,如下:

1func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
2    if handler == nil {
3        panic("http: nil handler")
4    }
5    mux.Handle(pattern, HandlerFunc(handler))
6}

在这个方法中,路由器又调用了Handle方法,注意这个Handle方法的第2个参数,将之前传入的handler这个响应方法强制转换成了HandlerFunc类型。

这个HandlerFunc类型到底是个什么呢?如下:

1type HandlerFunc func(ResponseWriter, *Request)

看来和我们定义的"/“的匿名函数的类型都差不多。但是!!! 这个HandlerFunc默认实现了ServeHTTP接口!这样HandlerFunc对象就有了ServeHTTP方法!如下:

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

接下来,我们返回去继续看muxHandle方法,也就是这段代码mux.Handle(pattern, HandlerFunc(handler))。这段代码做了哪些事呢?源码如下

 1// Handle registers the handler for the given pattern.
 2// If a handler already exists for pattern, Handle panics.
 3func (mux *ServeMux) Handle(pattern string, handler Handler) {
 4    mux.mu.Lock()
 5    defer mux.mu.Unlock()
 6
 7    if pattern == "" {
 8        panic("http: invalid pattern")
 9    }
10    if handler == nil {
11        panic("http: nil handler")
12    }
13    if _, exist := mux.m[pattern]; exist {
14        panic("http: multiple registrations for " + pattern)
15    }
16
17    if mux.m == nil {
18        mux.m = make(map[string]muxEntry)
19    }
20    e := muxEntry{h: handler, pattern: pattern}
21    mux.m[pattern] = e
22    if pattern[len(pattern)-1] == '/' {
23        mux.es = appendSorted(mux.es, e)
24    }
25
26    if pattern[0] != '/' {
27        mux.hosts = true
28    }
29}

主要就做了一件事,向DefaultServeMuxmap[string]muxEntry中增加对应的路由规则和handler

map[string]muxEntry是个什么鬼?

  • map是一个字典对象,它保存的是key-value

  • [string]表示这个字典的keystring类型的,这个key值会保存我们的路由规则。

  • muxEntry是一个实例对象,这个对象内保存了路由规则对应的处理方法。

  • mux.es 为模糊匹配 有长倒短排序 比如有路由/hello/ 访问/hello/world 时没有路由 会落到/hello/

找到相应代码,如下:

 1// 路由器
 2type ServeMux struct {
 3    mu    sync.RWMutex
 4    m     map[string]muxEntry
 5    es    []muxEntry // slice of entries sorted from longest to shortest.
 6    hosts bool       // whether any patterns contain hostnames
 7}
 8
 9type muxEntry struct {
10    h       Handler
11    pattern string
12}
13
14// 路由响应方法
15type Handler interface {
16    ServeHTTP(ResponseWriter, *Request)
17}

net/http 运行

第二部分主要就是研究这句代码err := http.ListenAndServe(":9001",nil),也就是ListenAndServe这个方法。从这个方法点进去,如下:

1func ListenAndServe(addr string, handler Handler) error {
2    server := &Server{Addr: addr, Handler: handler}
3    return server.ListenAndServe()
4}

在这个方法中,初始化了一个server对象,然后调用这个server对象的ListenAndServe方法,在这个方法中,如下:

 1func (srv *Server) ListenAndServe() error {
 2    if srv.shuttingDown() {
 3        return ErrServerClosed
 4    }
 5    addr := srv.Addr
 6    if addr == "" {
 7        addr = ":http"
 8    }
 9    ln, err := net.Listen("tcp", addr)
10    if err != nil {
11        return err
12    }
13    return srv.Serve(ln)
14}

在这个方法中,调用了net.Listen("tcp", addr),也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。

代码的最后,调用了srvServe方法,如下:

 1func (srv *Server) Serve(l net.Listener) error {
 2    if fn := testHookServerServe; fn != nil {
 3        fn(srv, l) // call hook with unwrapped listener
 4    }
 5
 6    origListener := l
 7    l = &onceCloseListener{Listener: l}
 8    defer l.Close()
 9
10    if err := srv.setupHTTP2_Serve(); err != nil {
11        return err
12    }
13
14    if !srv.trackListener(&l, true) {
15        return ErrServerClosed
16    }
17    defer srv.trackListener(&l, false)
18
19    baseCtx := context.Background()
20    if srv.BaseContext != nil {
21        baseCtx = srv.BaseContext(origListener)
22        if baseCtx == nil {
23            panic("BaseContext returned a nil context")
24        }
25    }
26
27    var tempDelay time.Duration // how long to sleep on accept failure
28
29    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
30    for {
31        rw, err := l.Accept()
32        if err != nil {
33            select {
34            case <-srv.getDoneChan():
35                return ErrServerClosed
36            default:
37            }
38            if ne, ok := err.(net.Error); ok && ne.Temporary() {
39                if tempDelay == 0 {
40                    tempDelay = 5 * time.Millisecond
41                } else {
42                    tempDelay *= 2
43                }
44                if max := 1 * time.Second; tempDelay > max {
45                    tempDelay = max
46                }
47                srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
48                time.Sleep(tempDelay)
49                continue
50            }
51            return err
52        }
53        connCtx := ctx
54        if cc := srv.ConnContext; cc != nil {
55            connCtx = cc(connCtx, rw)
56            if connCtx == nil {
57                panic("ConnContext returned nil")
58            }
59        }
60        tempDelay = 0
61        c := srv.newConn(rw)
62        c.setState(c.rwc, StateNew, runHooks) // before Serve can return
63        go c.serve(connCtx)
64    }
65}

最后3段代码比较重要,也是Go语言支持高并发的体现,如下:

1c := srv.newConn(rw)
2c.setState(c.rwc, StateNew, runHooks) // before Serve can return
3go c.serve(connCtx)

上面那一大坨代码,总体意思是进入方法后,首先开了一个for循环,在for循环内时刻Accept请求,请求来了之后,会为每个请求创建一个Conn,然后单独开启一个goroutine,把这个请求的数据当做参数扔给这个Conn去服务:go c.serve()。用户的每一次请求都是在一个新的goroutine去服务,每个请求间相互不影响。

connserve方法中,有一句代码很重要,如下:

1serverHandler{c.server}.ServeHTTP(w, w.req)

表示serverHandler也实现了ServeHTTP接口,ServeHTTP方法实现如下:

 1func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
 2    handler := sh.srv.Handler
 3    if handler == nil {
 4        handler = DefaultServeMux
 5    }
 6    if req.RequestURI == "*" && req.Method == "OPTIONS" {
 7        handler = globalOptionsHandler{}
 8    }
 9
10    if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {
11        var allowQuerySemicolonsInUse int32
12        req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {
13            atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)
14        }))
15        defer func() {
16            if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {
17                sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
18            }
19        }()
20    }
21
22    handler.ServeHTTP(rw, req)
23}

在这里如果handler为空(这个handler就可以理解为是我们自定义的路由器),就会使用系统默认的DefaultServeMux,代码的最后调用了DefaultServeMuxServeHTTP()

 1func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
 2    if r.RequestURI == "*" {
 3        if r.ProtoAtLeast(1, 1) {
 4            w.Header().Set("Connection", "close")
 5        }
 6        w.WriteHeader(StatusBadRequest)
 7        return
 8    }
 9    h, _ := mux.Handler(r)  //这里返回的h是Handler接口对象
10    h.ServeHTTP(w, r)  //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法
11}
 1func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
 2
 3    // CONNECT requests are not canonicalized.
 4    if r.Method == "CONNECT" {
 5        // If r.URL.Path is /tree and its handler is not registered,
 6        // the /tree -> /tree/ redirect applies to CONNECT requests
 7        // but the path canonicalization does not.
 8        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
 9            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
10        }
11
12        return mux.handler(r.Host, r.URL.Path)
13    }
14
15    // All other requests have any port stripped and path cleaned
16    // before passing to mux.handler.
17    host := stripHostPort(r.Host)
18    path := cleanPath(r.URL.Path)
19
20    // If the given path is /tree and its handler is not registered,
21    // redirect for /tree/.
22    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
23        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
24    }
25
26    if path != r.URL.Path {
27        _, pattern = mux.handler(host, path)
28        u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
29        return RedirectHandler(u.String(), StatusMovedPermanently), pattern
30    }
31
32    return mux.handler(host, r.URL.Path)
33}
34
35
36func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
37    mux.mu.RLock()
38    defer mux.mu.RUnlock()
39
40    // Host-specific pattern takes precedence over generic ones
41    if mux.hosts {
42        h, pattern = mux.match(host + path)
43    }
44    if h == nil {
45        h, pattern = mux.match(path)
46    }
47    if h == nil {
48        h, pattern = NotFoundHandler(), ""
49    }
50    return
51}
52
53func (mux *ServeMux) match(path string) (h Handler, pattern string) {
54    // Check for exact match first.
55    v, ok := mux.m[path]
56    if ok {
57        return v.h, v.pattern
58    }
59
60    // Check for longest valid match.  mux.es contains all patterns
61    // that end in / sorted from longest to shortest.
62    for _, e := range mux.es {
63        if strings.HasPrefix(path, e.pattern) {
64            return e.h, e.pattern
65        }
66    }
67    return nil, ""
68}

它会根据用户请求的URL到路由器里面存储的map中匹配,匹配成功就会返回存储的handler,调用这个handlerServeHTTP()就可以执行到相应的处理方法了,这个处理方法实际上就是我们刚开始定义的sayHello(),只不过这个sayHello()HandlerFunc又包了一层,因为HandlerFunc实现了ServeHTTP接口,所以在调用HandlerFunc对象的ServeHTTP()时,实际上在ServeHTTP ()的内部调用了我们的sayHello()

总结

  1. 调用http.ListenAndServe(":9090",nil)
  2. 实例化server
  3. 调用serverListenAndServe()
  4. 调用serverServe方法,开启for循环,在循环中Accept请求
  5. 对每一个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
  6. 读取每个请求的内容c.readRequest()
  7. 调用serverHandlerServeHTTP(),如果handler为空,就把handler设置为系统默认的路由器DefaultServeMux
  8. 调用handlerServeHTTP() =>实际上是调用了DefaultServeMuxServeHTTP()
  9. ServeHTTP()中会调用路由对应处理handler
  10. 在路由对应处理handler中会执行sayHello()

有一个需要注意的点: DefaultServeMux和路由对应的处理方法handler都实现了ServeHTTP接口,他们俩都有ServeHTTP方法,但是方法要达到的目的不同,在DefaultServeMuxServeHttp()里会执行路由对应的处理handlerServeHttp()

自定义个简单的路由

 1package mux
 2
 3import (
 4    "net/http"
 5    "strings"
 6)
 7
 8type muxEntry struct {
 9    h TesthandleFunc
10}
11
12type TesthandleFunc func(http.ResponseWriter, *http.Request)
13
14type TestHandler struct {
15    routes map[string]map[string]muxEntry
16}
17
18
19func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
20    method := strings.ToUpper(r.Method)
21    path := r.URL.Path
22    if route, ok := h.routes[method]; ok {
23        if entry, ok := route[path]; ok {
24            entry.h(w, r)
25            return
26        }
27    }
28    w.WriteHeader(http.StatusNotFound)
29}
30
31func Newhandler() *TestHandler {
32    return &TestHandler{routes: make(map[string]map[string]muxEntry)}
33}
34
35func (h *TestHandler) Handle(method, path string, handler TesthandleFunc) {
36    method = strings.ToUpper(method)
37    if _, ok := h.routes[method]; !ok {
38        h.routes[method] = make(map[string]muxEntry)
39    }
40    h.routes[method][path] = muxEntry{handler}
41}
 1package main
 2
 3import (
 4    "fmt"
 5    "net/http"
 6    "study/mux"
 7)
 8
 9func main() {
10    handler := mux.Newhandler()
11    handler.Handle("GET", "/hello", func(rw http.ResponseWriter, r *http.Request) {
12        rw.Write([]byte("Hello World"))
13    })
14    handler.Handle("Post", "/hello/world", func(rw http.ResponseWriter, r *http.Request) {
15        fmt.Fprintln(rw, "你好")
16    })
17    http.ListenAndServe(":9002", handler)
18}

自定义context

 1package router
 2
 3import (
 4    "encoding/json"
 5    "net/http"
 6    "strings"
 7)
 8
 9type Context struct {
10    w http.ResponseWriter
11    r *http.Request
12}
13
14func (c *Context) Json(code int, v interface{}) {
15    c.w.Header().Set("Content-Type", "application/json")
16    c.w.WriteHeader(code)
17    s, _ := json.Marshal(v)
18    c.w.Write(s)
19}
20
21type Routerfunc func(c *Context)
22
23type RouterHandler struct {
24    routes map[string]map[string]Routerfunc
25}
26
27func NewRouterHandler() *RouterHandler {
28    return &RouterHandler{routes: make(map[string]map[string]Routerfunc)}
29}
30
31func (h *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
32    method := strings.ToUpper(r.Method)
33    path := r.URL.Path
34    c := &Context{w: w, r: r}
35    if route, ok := h.routes[method]; ok {
36        if h, ok := route[path]; ok {
37            h(c)
38            return
39        }
40    }
41    w.WriteHeader(http.StatusNotFound)
42}
43
44func (h *RouterHandler) Handle(method, path string, handler Routerfunc) {
45    method = strings.ToUpper(method)
46    if _, ok := h.routes[method]; !ok {
47        h.routes[method] = make(map[string]Routerfunc)
48    }
49    h.routes[method][path] = handler
50}
51
52func (r *RouterHandler) Run(addr string) error {
53    return http.ListenAndServe(addr, r)
54}

Gin

 1type Engine struct {
 2    RouterGroup
 3
 4    pool     sync.Pool
 5    trees    methodTrees
 6}// trie
 7
 8type RouterGroup struct {
 9    basePath string
10    engine   *Engine
11}
12
13func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
14    c := engine.pool.Get().(*Context) // 从pool 拿出一个context
15    c.writermem.reset(w) // 记录http.ResponseWriter 及 *http.Request
16    c.Request = req
17    c.reset() // 重置上一个留下的值
18
19    engine.handleHTTPRequest(c)
20
21    engine.pool.Put(c) // 把用完的context放回池子
22}
23// get: /bac

5BD3C0FE-8543-42AE-AA05-D60B7A249A09.png

添加路由

1func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
2    absolutePath := group.calculateAbsolutePath(relativePath)
3    handlers = group.combineHandlers(handlers)
4    group.engine.addRoute(httpMethod, absolutePath, handlers)
5    return group.returnObj()
6}

Context

 1type Context struct {
 2    Request   *http.Request
 3    Writer    ResponseWriter
 4
 5    Params   Params
 6    handlers HandlersChain
 7    index    int8
 8    fullPath string
 9
10    engine       *Engine
11    params       *Params
12    skippedNodes *[]skippedNode
13
14    // This mutex protect Keys map
15    mu sync.RWMutex
16
17    // Keys is a key/value pair exclusively for the context of each request.
18    Keys map[string]interface{}
19
20    // Errors is a list of errors attached to all the handlers/middlewares who used this context.
21    Errors errorMsgs
22
23    // Accepted defines a list of manually accepted formats for content negotiation.
24    Accepted []string
25
26    // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
27    queryCache url.Values
28
29    // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
30    // or PUT body parameters.
31    formCache url.Values
32
33    // SameSite allows a server to define a cookie attribute making it impossible for
34    // the browser to send this cookie along with cross-site requests.
35    sameSite http.SameSite
36}
37
38func (c *Context) Next() {
39    c.index++
40    for c.index < int8(len(c.handlers)) {
41        c.handlers[c.index](c)
42        c.index++
43    }
44}
 1func (engine *Engine) handleHTTPRequest(c *Context) {
 2    httpMethod := c.Request.Method
 3    rPath := c.Request.URL.Path
 4    unescape := false
 5    if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
 6        rPath = c.Request.URL.RawPath
 7        unescape = engine.UnescapePathValues
 8    }
 9
10    if engine.RemoveExtraSlash {
11        rPath = cleanPath(rPath)
12    }
13
14    // Find root of the tree for the given HTTP method
15    t := engine.trees
16    for i, tl := 0, len(t); i < tl; i++ {
17        if t[i].method != httpMethod {
18            continue
19        }
20        root := t[i].root
21        // Find route in tree
22        value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
23        if value.params != nil {
24            c.Params = *value.params
25        }
26        if value.handlers != nil {
27            c.handlers = value.handlers
28            c.fullPath = value.fullPath
29            c.Next()
30            c.writermem.WriteHeaderNow()
31            return
32        }
33        if httpMethod != http.MethodConnect && rPath != "/" {
34            if value.tsr && engine.RedirectTrailingSlash {
35                redirectTrailingSlash(c)
36                return
37            }
38            if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
39                return
40            }
41        }
42        break
43    }
44
45    if engine.HandleMethodNotAllowed {
46        for _, tree := range engine.trees {
47            if tree.method == httpMethod {
48                continue
49            }
50            if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
51                c.handlers = engine.allNoMethod
52                serveError(c, http.StatusMethodNotAllowed, default405Body)
53                return
54            }
55        }
56    }
57    c.handlers = engine.allNoRoute
58    serveError(c, http.StatusNotFound, default404Body)
59}