nethttp和gin 路由
net/http 路由注册
func test1() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello world!") }) err := http.ListenAndServe(":9001", nil) if err != nil { log.Fatal("ListenAndServer:", err) }}
在使用ListenAndServe
这个方法时,系统就会给我们指派一个路由器,DefaultServeMux
是系统默认使用的路由器,如果ListenAndServe
这个方法的第2个参数传入nil,系统就会默认使用DefaultServeMux
。当然,这里也可以传入自定义的路由器。
先看http.HandleFunc("/", ...)
,从HandleFunc
方法点进去,如下:
【资料图】
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
在这里调用了DefaultServeMux
的HandleFunc
方法,这个方法有两个参数,pattern
是匹配的路由规则,handler
表示这个路由规则对应的处理方法,并且这个处理方法有两个参数。
在我们书写的代码示例中,pattern
对应/
,handler
对应sayHello
,当我们在浏览器中输入http://localhost:9001
时,就会触发匿名函数。
我们再顺着DefaultServeMux
的HandleFunc
方法继续点下去,如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler))}
在这个方法中,路由器又调用了Handle
方法,注意这个Handle
方法的第2个参数,将之前传入的handler
这个响应方法强制转换成了HandlerFunc
类型。
这个HandlerFunc
类型到底是个什么呢?如下:
type HandlerFunc func(ResponseWriter, *Request)
看来和我们定义的"/"的匿名函数的类型都差不多。但是!!! 这个HandlerFunc
默认实现了ServeHTTP
接口!这样HandlerFunc
对象就有了ServeHTTP
方法!如下:
// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
接下来,我们返回去继续看mux
的Handle
方法,也就是这段代码mux.Handle(pattern, HandlerFunc(handler))
。这段代码做了哪些事呢?源码如下
// 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] = e if pattern[len(pattern)-1] == "/" { mux.es = appendSorted(mux.es, e) } if pattern[0] != "/" { mux.hosts = true }}
主要就做了一件事,向DefaultServeMux
的map[string]muxEntry
中增加对应的路由规则和handler
。
map[string]muxEntry
是个什么鬼?
map
是一个字典对象,它保存的是key-value
。[string]
表示这个字典的key
是string
类型的,这个key
值会保存我们的路由规则。muxEntry
是一个实例对象,这个对象内保存了路由规则对应的处理方法。mux.es
为模糊匹配 有长倒短排序 比如有路由/hello/
访问/hello/world
时没有路由 会落到/hello/
上
找到相应代码,如下:
// 路由器type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames}type muxEntry struct { h Handler pattern string}// 路由响应方法type Handler interface { ServeHTTP(ResponseWriter, *Request)}
net/http 运行
第二部分主要就是研究这句代码err := http.ListenAndServe(":9001",nil)
,也就是ListenAndServe
这个方法。从这个方法点进去,如下:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
在这个方法中,初始化了一个server
对象,然后调用这个server
对象的ListenAndServe
方法,在这个方法中,如下:
func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(ln)}
在这个方法中,调用了net.Listen("tcp", addr)
,也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。
代码的最后,调用了srv
的Serve
方法,如下:
func (srv *Server) Serve(l net.Listener) error { if fn := testHookServerServe; fn != nil { fn(srv, l) // call hook with unwrapped listener } origListener := l l = &onceCloseListener{Listener: l} defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } if !srv.trackListener(&l, true) { return ErrServerClosed } defer srv.trackListener(&l, false) baseCtx := context.Background() if srv.BaseContext != nil { baseCtx = srv.BaseContext(origListener) if baseCtx == nil { panic("BaseContext returned a nil context") } } var tempDelay time.Duration // how long to sleep on accept failure ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, err := l.Accept() if err != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } return err } connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew, runHooks) // before Serve can return go c.serve(connCtx) }}
最后3段代码比较重要,也是Go语言支持高并发的体现,如下:
c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can returngo c.serve(connCtx)
上面那一大坨代码,总体意思是进入方法后,首先开了一个for
循环,在for
循环内时刻Accept请求,请求来了之后,会为每个请求创建一个Conn
,然后单独开启一个goroutine
,把这个请求的数据当做参数扔给这个Conn
去服务:go c.serve()
。用户的每一次请求都是在一个新的goroutine
去服务,每个请求间相互不影响。
在conn
的serve
方法中,有一句代码很重要,如下:
serverHandler{c.server}.ServeHTTP(w, w.req)
表示serverHandler
也实现了ServeHTTP
接口,ServeHTTP
方法实现如下:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { var allowQuerySemicolonsInUse int32 req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) })) defer func() { if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { 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") } }() } handler.ServeHTTP(rw, req)}
在这里如果handler
为空(这个handler
就可以理解为是我们自定义的路由器),就会使用系统默认的DefaultServeMux
,代码的最后调用了DefaultServeMux
的ServeHTTP()
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) //这里返回的h是Handler接口对象 h.ServeHTTP(w, r) //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { // If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not. if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, // redirect for /tree/. if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } if path != r.URL.Path { _, pattern = mux.handler(host, path) u := &url.URL{Path: path, RawQuery: r.URL.RawQuery} return RedirectHandler(u.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path)}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 ones if mux.hosts { h, pattern = mux.match(host + path) } if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return}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, ""}
它会根据用户请求的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()
。
自定义个简单的路由
package muximport ( "net/http" "strings")type muxEntry struct { h TesthandleFunc}type TesthandleFunc func(http.ResponseWriter, *http.Request)type TestHandler struct { routes map[string]map[string]muxEntry}func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path if route, ok := h.routes[method]; ok { if entry, ok := route[path]; ok { entry.h(w, r) return } } w.WriteHeader(http.StatusNotFound)}func Newhandler() *TestHandler { return &TestHandler{routes: make(map[string]map[string]muxEntry)}}func (h *TestHandler) Handle(method, path string, handler TesthandleFunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]muxEntry) } h.routes[method][path] = muxEntry{handler}}
package mainimport ( "fmt" "net/http" "study/mux")func main() { handler := mux.Newhandler() handler.Handle("GET", "/hello", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte("Hello World")) }) handler.Handle("Post", "/hello/world", func(rw http.ResponseWriter, r *http.Request) { fmt.Fprintln(rw, "你好") }) http.ListenAndServe(":9002", handler)}
自定义context
package routerimport ( "encoding/json" "net/http" "strings")type Context struct { w http.ResponseWriter r *http.Request}func (c *Context) Json(code int, v interface{}) { c.w.Header().Set("Content-Type", "application/json") c.w.WriteHeader(code) s, _ := json.Marshal(v) c.w.Write(s)}type Routerfunc func(c *Context)type RouterHandler struct { routes map[string]map[string]Routerfunc}func NewRouterHandler() *RouterHandler { return &RouterHandler{routes: make(map[string]map[string]Routerfunc)}}func (h *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path c := &Context{w: w, r: r} if route, ok := h.routes[method]; ok { if h, ok := route[path]; ok { h(c) return } } w.WriteHeader(http.StatusNotFound)}func (h *RouterHandler) Handle(method, path string, handler Routerfunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]Routerfunc) } h.routes[method][path] = handler}func (r *RouterHandler) Run(addr string) error { return http.ListenAndServe(addr, r)}
Gin
type Engine struct { RouterGroup pool sync.Pool trees methodTrees}// trietype RouterGroup struct { basePath string engine *Engine}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) // 从pool 拿出一个context c.writermem.reset(w) // 记录http.ResponseWriter 及 *http.Request c.Request = req c.reset() // 重置上一个留下的值 engine.handleHTTPRequest(c) engine.pool.Put(c) // 把用完的context放回池子}// get: /bac
添加路由
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj()}
Context
type Context struct { Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 fullPath string engine *Engine params *Params skippedNodes *[]skippedNode // This mutex protect Keys map mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() queryCache url.Values // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values // SameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite}func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { c.handlers[c.index](c) c.index++ }}
func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path unescape := false if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 { rPath = c.Request.URL.RawPath unescape = engine.UnescapePathValues } if engine.RemoveExtraSlash { rPath = cleanPath(rPath) } // Find root of the tree for the given HTTP method t := engine.trees for i, tl := 0, len(t); i < tl; i++ { if t[i].method != httpMethod { continue } root := t[i].root // Find route in tree value := root.getValue(rPath, c.params, c.skippedNodes, unescape) if value.params != nil { c.Params = *value.params } if value.handlers != nil { c.handlers = value.handlers c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() return } if httpMethod != http.MethodConnect && rPath != "/" { if value.tsr && engine.RedirectTrailingSlash { redirectTrailingSlash(c) return } if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { return } } break } if engine.HandleMethodNotAllowed { for _, tree := range engine.trees { if tree.method == httpMethod { continue } if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { c.handlers = engine.allNoMethod serveError(c, http.StatusMethodNotAllowed, default405Body) return } } } c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body)}