本篇次要介绍gin服务启动过程的源码

Run() 启动入口

咱们的程序都是通过调用Run函数来启动gin的实例,上面来看一下Run的源码:

func (engine *Engine) Run(addr ...string) (err error) {    defer func() { debugPrintError(err) }()    // 解析服务地址    address := resolveAddress(addr)    debugPrint("Listening and serving HTTP on %s\n", address)    // 此处会block    err = http.ListenAndServe(address, engine)    return}

该办法其实是对http.ListenAndServe的简略封装,以下逻辑就进入net/http包中了,持续往下看:

func ListenAndServe(addr string, handler Handler) error {    // 配置tcp的监听地址和监听到来后的处理函数    server := &Server{Addr: addr, Handler: handler}    // 持续调用。。。    return server.ListenAndServe()}// Handler定义type Handler interface {    // 申请到来后的解决逻辑,gin框架会本人实现    ServeHTTP(ResponseWriter, *Request)}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)}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        // 创立一个新连贯,一个conn对象,代表服务端的http连贯,里边蕴含该次申请的数据        c := srv.newConn(rw)        c.setState(c.rwc, StateNew) // before Serve can return        // 开启一个goroutine解决申请,将上下文传递进去,高并发的保障        go c.serve(connCtx)    }}// serve函数只摘取最重要的一行func (c *conn) serve(ctx context.Context) {    ...    // 这里就会调用gin框架实现的ServeHTTP逻辑    serverHandler{c.server}.ServeHTTP(w, w.req)    ...}

ServeHTTP 逻辑

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {    // 从对象池子里获取一个Context对象,池子里有能够间接拿来用,没有则创立一个新的对象返回    c := engine.pool.Get().(*Context)    // 把申请的相干数据都写入Context    c.writermem.reset(w)    c.Request = req    c.reset()    // 申请解决逻辑    engine.handleHTTPRequest(c)    // 应用结束,放回池子,复用    engine.pool.Put(c)}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    }    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        // 从树中找到路由对应的函数解决链和参数        handlers, params, tsr := root.getValue(rPath, c.Params, unescape)        if handlers != nil {            c.handlers = handlers            c.Params = params            // 按程序执行handler办法,能够配合在中间件中应用            c.Next()            // handlers中的所有函数都处理完毕后,该返回了            c.writermem.WriteHeaderNow()            return        }        if httpMethod != "CONNECT" && rPath != "/" {            if 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 handlers, _, _ := tree.root.getValue(rPath, nil, unescape); handlers != nil {                c.handlers = engine.allNoMethod                serveError(c, http.StatusMethodNotAllowed, default405Body)                return            }        }    }    c.handlers = engine.allNoRoute    serveError(c, http.StatusNotFound, default404Body)}// 看一下Next办法,很简略func (c *Context) Next() {    c.index++    for c.index < int8(len(c.handlers)) {        c.handlers[c.index](c)        c.index++    }}

总结

  1. 启动流程很清晰,通过net/http包监听申请,外围逻辑就是一个死循环,有限期待,当有申请达到指定端口时,启动一个goroutine异步解决该申请。
  2. 申请的解决逻辑应用的是gin框架本人实现的ServeHTTP函数,gin对Conext的封装和复用,也是一大亮点。
  3. context的Next办法在中间件中的应用,能够实现后置中间件。