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}
在这里调用了DefaultServeMux
的HandleFunc
方法,这个方法有两个参数,pattern
是匹配的路由规则,handler
表示这个路由规则对应的处理方法,并且这个处理方法有两个参数。
在我们书写的代码示例中,pattern
对应/
,handler
对应sayHello
,当我们在浏览器中输入http://localhost:9001
时,就会触发匿名函数。
我们再顺着DefaultServeMux
的HandleFunc
方法继续点下去,如下:
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}
接下来,我们返回去继续看mux
的Handle
方法,也就是这段代码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}
主要就做了一件事,向DefaultServeMux
的map[string]muxEntry
中增加对应的路由规则和handler
。
map[string]muxEntry
是个什么鬼?
-
map
是一个字典对象,它保存的是key-value
。 -
[string]
表示这个字典的key
是string
类型的,这个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协议搭建了一个服务,然后监控我们设置的端口。
代码的最后,调用了srv
的Serve
方法,如下:
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
去服务,每个请求间相互不影响。
在conn
的serve
方法中,有一句代码很重要,如下:
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
,代码的最后调用了DefaultServeMux
的ServeHTTP()
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
,调用这个handler
的ServeHTTP()
就可以执行到相应的处理方法了,这个处理方法实际上就是我们刚开始定义的sayHello()
,只不过这个sayHello()
被HandlerFunc
又包了一层,因为HandlerFunc
实现了ServeHTTP
接口,所以在调用HandlerFunc
对象的ServeHTTP()
时,实际上在ServeHTTP ()
的内部调用了我们的sayHello()
。
总结
- 调用
http.ListenAndServe(":9090",nil)
- 实例化
server
- 调用
server
的ListenAndServe()
- 调用
server
的Serve
方法,开启for
循环,在循环中Accept请求 - 对每一个请求实例化一个
Conn
,并且开启一个goroutine
为这个请求进行服务go c.serve()
- 读取每个请求的内容
c.readRequest()
- 调用
serverHandler
的ServeHTTP()
,如果handler
为空,就把handler
设置为系统默认的路由器DefaultServeMux
- 调用
handler
的ServeHTTP()
=>实际上是调用了DefaultServeMux
的ServeHTTP()
- 在
ServeHTTP()
中会调用路由对应处理handler
- 在路由对应处理
handler
中会执行sayHello()
有一个需要注意的点: DefaultServeMux
和路由对应的处理方法handler
都实现了ServeHTTP
接口,他们俩都有ServeHTTP
方法,但是方法要达到的目的不同,在DefaultServeMux
的ServeHttp()
里会执行路由对应的处理handler
的ServeHttp()
。
自定义个简单的路由
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
添加路由
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}