gin 概览

想弄清楚 gin, 需要弄明白以下几个问题:

  • request数据是如何流转的
  • gin框架到底扮演了什么角色
  • 请求从gin流入net/http, 最后又是如何回到gin中
  • gin的context为何能承担起来复杂的需求
  • gin的路由算法
  • gin的中间件是什么
  • gin的Engine具体是个什么东西
  • net/http的requeset, response都提供了哪些有用的东西

从gin的官方第一个demo入手.

  1. package main
  2. import "github.com/gin-gonic/gin"
  3. func main() {
  4. r := gin.Default()
  5. r.GET("/ping", func(c *gin.Context) {
  6. c.JSON(200, gin.H{
  7. "message": "pong",
  8. })
  9. })
  10. r.Run() // listen and serve on 0.0.0.0:8080
  11. }

r.Run() 的源码:

  1. func (engine *Engine) Run(addr ...string) (err error) {
  2. defer func() { debugPrintError(err) }()
  3. trustedCIDRs, err := engine.prepareTrustedCIDRs()
  4. if err != nil {
  5. return err
  6. }
  7. engine.trustedCIDRs = trustedCIDRs
  8. address := resolveAddress(addr)
  9. debugPrint("Listening and serving HTTP on %s\n", address)
  10. err = http.ListenAndServe(address, engine)
  11. return
  12. }

看到开始调用的是 http.ListenAndServe(address, engine), 这个函数是net/http的函数, 然后请求数据就在net/http开始流转.

net/http 是如何建立 socket 的

先不使用gin, 直接使用net/http来处理http请求

  1. func main() {
  2. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  3. w.Write([]byte("Hello World"))
  4. })
  5. if err := http.ListenAndServe(":8000", nil); err != nil {
  6. fmt.Println("start http server fail:", err)
  7. }
  8. }

从图上可以看出, 不管server代码如何封装, 都离不开bind,listen,accept这些函数. 就从上面这个简单的demo入手查看源码.

注册路由

  1. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  2. w.Write([]byte("Hello World"))
  3. })

这段代码是在注册一个路由及这个路由的handler到DefaultServeMux中

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

/usr/local/go/src/net/http/server.go:2453

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

可以看到这个路由注册太过简单了, 也就给gin, iris, echo等框架留下了扩展的空间, 后面详细说这个东西

服务监听及响应

上面路由已经注册到net/http了, 下面就该如何建立socket了, 以及最后又如何取到已经注册到的路由, 将正确的响应信息从handler中取出来返回给客户端

1.创建 socket

  1. if err := http.ListenAndServe(":8000", nil); err != nil {
  2. fmt.Println("start http server fail:", err)
  3. }
  1. func ListenAndServe(addr string, handler Handler) error {
  2. server := &Server{Addr: addr, Handler: handler}
  3. return server.ListenAndServe()
  4. }
  1. func (srv *Server) ListenAndServe() error {
  2. if srv.shuttingDown() {
  3. return ErrServerClosed
  4. }
  5. addr := srv.Addr
  6. if addr == "" {
  7. addr = ":http"
  8. }
  9. // 这里创建了socket,处理连接
  10. ln, err := net.Listen("tcp", addr)
  11. if err != nil {
  12. return err
  13. }
  14. return srv.Serve(ln)
  15. }

2.Accept 等待客户端链接

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

可以看看srv.newConn干了啥

  1. // Create new connection from rwc.
  2. func (srv *Server) newConn(rwc net.Conn) *conn {
  3. c := &conn{
  4. server: srv,
  5. rwc: rwc,
  6. }
  7. if debugServerConnections {
  8. c.rwc = newLoggingConn("server", c.rwc)
  9. }
  10. return c
  11. }

它比较简单,这里返回的conn 对net.Conn进行了一次包装, conn表示HTTP连接的服务器端

3. 提供回调接口 ServeHTTP

  1. // Serve a new connection.
  2. func (c *conn) serve(ctx context.Context) {
  3. c.remoteAddr = c.rwc.RemoteAddr().String()
  4. ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
  5. defer func() {
  6. if err := recover(); err != nil && err != ErrAbortHandler {
  7. const size = 64 << 10
  8. buf := make([]byte, size)
  9. buf = buf[:runtime.Stack(buf, false)]
  10. c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
  11. }
  12. if !c.hijacked() {
  13. c.close()
  14. c.setState(c.rwc, StateClosed, runHooks)
  15. }
  16. }()
  17. // 如果是tls连接,要进行Handshake
  18. if tlsConn, ok := c.rwc.(*tls.Conn); ok {
  19. ....
  20. }
  21. // HTTP/1.x from here on.
  22. ctx, cancelCtx := context.WithCancel(ctx)
  23. c.cancelCtx = cancelCtx
  24. defer cancelCtx()
  25. c.r = &connReader{conn: c}
  26. c.bufr = newBufioReader(c.r)
  27. c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
  28. for {
  29. w, err := c.readRequest(ctx)
  30. if c.r.remain != c.server.initialReadLimitSize() {
  31. // If we read any bytes off the wire, we're active.
  32. c.setState(c.rwc, StateActive, runHooks)
  33. }
  34. // 读取数据失败的处理
  35. if err != nil {
  36. const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
  37. switch {
  38. case err == errTooLarge:
  39. // Their HTTP client may or may not be
  40. // able to read this if we're
  41. // responding to them and hanging up
  42. // while they're still writing their
  43. // request. Undefined behavior.
  44. const publicErr = "431 Request Header Fields Too Large"
  45. fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
  46. c.closeWriteAndWait()
  47. return
  48. case isUnsupportedTEError(err):
  49. // Respond as per RFC 7230 Section 3.3.1 which says,
  50. // A server that receives a request message with a
  51. // transfer coding it does not understand SHOULD
  52. // respond with 501 (Unimplemented).
  53. code := StatusNotImplemented
  54. // We purposefully aren't echoing back the transfer-encoding's value,
  55. // so as to mitigate the risk of cross side scripting by an attacker.
  56. fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s%sUnsupported transfer encoding", code, StatusText(code), errorHeaders)
  57. return
  58. case isCommonNetReadError(err):
  59. return // don't reply
  60. default:
  61. if v, ok := err.(statusError); ok {
  62. fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s: %s%s%d %s: %s", v.code, StatusText(v.code), v.text, errorHeaders, v.code, StatusText(v.code), v.text)
  63. return
  64. }
  65. publicErr := "400 Bad Request"
  66. fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
  67. return
  68. }
  69. }
  70. // Expect 100 Continue support
  71. req := w.req
  72. if req.expectsContinue() {
  73. if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
  74. // Wrap the Body reader with one that replies on the connection
  75. req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
  76. w.canWriteContinue.setTrue()
  77. }
  78. } else if req.Header.get("Expect") != "" {
  79. w.sendExpectationFailed()
  80. return
  81. }
  82. c.curReq.Store(w)
  83. if requestBodyRemains(req.Body) {
  84. registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
  85. } else {
  86. w.conn.r.startBackgroundRead()
  87. }
  88. serverHandler{c.server}.ServeHTTP(w, w.req) // 处理http请求
  89. w.cancelCtx()
  90. if c.hijacked() {
  91. return
  92. }
  93. w.finishRequest()
  94. if !w.shouldReuseConnection() {
  95. if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
  96. c.closeWriteAndWait()
  97. }
  98. return
  99. }
  100. c.setState(c.rwc, StateIdle, runHooks)
  101. c.curReq.Store((*response)(nil))
  102. if !w.conn.server.doKeepAlives() {
  103. // We're in shutdown mode. We might've replied
  104. // to the user without "Connection: close" and
  105. // they might think they can send another
  106. // request, but such is life with HTTP/1.1.
  107. return
  108. }
  109. if d := c.server.idleTimeout(); d != 0 {
  110. c.rwc.SetReadDeadline(time.Now().Add(d))
  111. if _, err := c.bufr.Peek(4); err != nil {
  112. return
  113. }
  114. }
  115. c.rwc.SetReadDeadline(time.Time{})
  116. }
  117. }
  1. func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  2. handler := sh.srv.Handler
  3. if handler == nil {
  4. // http.ListenAndServe(":8000", nil),当handler为nil时,默认用的是DefaultServeMux
  5. handler = DefaultServeMux
  6. }
  7. if req.RequestURI == "*" && req.Method == "OPTIONS" {
  8. handler = globalOptionsHandler{}
  9. }
  10. handler.ServeHTTP(rw, req)
  11. }

4. 回调到实际要执行的 ServeHTTP

  1. type Handler interface {
  2. ServeHTTP(ResponseWriter, *Request)
  3. }

我们已知handler其实就是DefaultServeMux,因此调用

  1. func (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) // 这里在路由表中找到HandlerFunc
  10. h.ServeHTTP(w, r)
  11. }

最后再调用ServeHTTP

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


这基本是整个过程的代码了.

  1. ln, err := net.Listen(“tcp”, addr)做了初试化了socket, bind, listen的操作.
  2. rw, e := l.Accept()进行accept, 等待客户端进行连接
  3. go c.serve(ctx) 启动新的goroutine来处理本次请求. 同时主goroutine继续等待客户端连接, 进行高并发操作
  4. h, _ := mux.Handler(r) 获取注册的路由, 然后拿到这个路由的handler, 然后将处理结果返回给客户端

从这里也能够看出来, net/http基本上提供了全套的服务.

为什么会出现很多go框架

  1. // Find a handler on a handler map given a path string.
  2. // Most-specific (longest) pattern wins.
  3. func (mux *ServeMux) match(path string) (h Handler, pattern string) {
  4. // Check for exact match first.
  5. v, ok := mux.m[path]
  6. if ok {
  7. return v.h, v.pattern
  8. }
  9. // Check for longest valid match. mux.es contains all patterns
  10. // that end in / sorted from longest to shortest.
  11. for _, e := range mux.es {
  12. if strings.HasPrefix(path, e.pattern) {
  13. return e.h, e.pattern
  14. }
  15. }
  16. return nil, ""
  17. }

这段函数可以看出来, 匹配规则过于简单, 当能匹配到路由的时候就返回其对应的handler, 当不能匹配到时就返回/. net/http的路由匹配根本就不符合 RESTful 的规则,遇到稍微复杂一点的需求时,这个简单的路由匹配规则简直就是噩梦。
所以基本所有的go框架干的最主要的一件事情就是重写net/http的route。我们直接说 gin就是一个 httprouter 也不过分, 当然gin也提供了其他比较主要的功能, 后面会一一介绍。
综述, net/http基本已经提供http服务的70%的功能, 那些号称贼快的go框架, 基本上都是提供一些功能, 让我们能够更好的处理客户端发来的请求. 如果你有兴趣的话,也可以基于 net/http 做一个 Go 框架出来。