Gin 框架源码大体学习

Gin 服务框架服务端使用示例:

package main

import "github.com/gin-gonic/gin"

func main(){
    // 建立http路由
    router := gin.Default()
    router.GET("/gin/test/", func(context *gin.Context) {
        context.JSON(200,gin.H{
            "msg":"success",
        })
    })
    router.Run(":6789")
}

建立Gin路由

返回一个“引擎“,定义其日志组件和panic处理组件

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
    engine := New()
    // 默认只开起日志和异常恢复的中间件
    engine.Use(Logger(), Recovery())
    return engine
}

// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash:  true
// - RedirectFixedPath:      false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP:    true
// - UseRawPath:             false
// - UnescapePathValues:     true
func New() *Engine {
    engine := &Engine{
        // 定义根路径的handler
        RouterGroup: RouterGroup{
            Handlers: nil,
            basePath: "/",
            root:     true,
        },
        // 定义模版 map[string]interface{}
        FuncMap:                template.FuncMap{},
        RedirectTrailingSlash:  true,
        RedirectFixedPath:      false,
        HandleMethodNotAllowed: false,
        ForwardedByClientIP:    true,
        AppEngine:              defaultAppEngine,
        UseRawPath:             false,
        UnescapePathValues:     true,
        MaxMultipartMemory:     defaultMultipartMemory,
        trees:                  make(methodTrees, 0, 9),
        delims:                 render.Delims{Left: "{{", Right: "}}"},
        secureJsonPrefix:       "while(1);",
    }
    engine.RouterGroup.engine = engine
    // sync.Pool
    engine.pool.New = func() interface{} {
        // 分配新context,实际上就是缓冲区
        return engine.allocateContext()
    }
    return engine
}

router 建立路由


// 定义所有路由
type methodTrees []methodTree
type methodTree struct {
    method string
    root   *node
}

type Engine struct {
    RouterGroup
    ... 
    trees            methodTrees
    pool             sync.Pool // gin context 缓存池,可reuse
    ...
}

type RouterGroup struct {
    // 中间件数组
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}

// 注册中间件
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}

// 注册访问路由
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
    group.handle("GET", relativePath, handlers)
    group.handle("POST", relativePath, handlers)
    group.handle("PUT", relativePath, handlers)
    group.handle("PATCH", relativePath, handlers)
    group.handle("HEAD", relativePath, handlers)
    group.handle("OPTIONS", relativePath, handlers)
    group.handle("DELETE", relativePath, handlers)
    group.handle("CONNECT", relativePath, handlers)
    group.handle("TRACE", relativePath, handlers)
    return group.returnObj()
}

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()
}

// 把新路由加入树中,每个方法是一个树的根节点
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    ...
    root := engine.trees.get(method)
    if root == nil {
        root = new(node)
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    // 大体形成类似于字典树[路径=>handlers]
    // 具体实现逻辑可参考 github.com/gin-gonic/gin/tree.go,httprouter相关参考:http://www.okyes.me/2016/05/08/httprouter.html
    root.addRoute(path, handlers)
}

Run Gin Server

// 相当于go net/http 封装
func (engine *Engine) Run(addr ...string) (err error) {
    defer func() { debugPrintError(err) }()
    address := resolveAddress(addr)
    // gin engine 实现了net/http的Handler,相当于gin自己做路由分发
    err = http.ListenAndServe(address, engine)
    return
}

// net/http 监听端口
func ListenAndServe(addr string, handler Handler) error {
    // new server
    server := &Server{Addr: addr, Handler: handler}
    // listen the port
    return server.ListenAndServe() // 底层逻辑调用Server函数,监听端口+接收可读事件+goroutine处理请求
}

// 监听端口+接收可读事件+goroutine处理请求
func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()

    // 没有事件到达时,sleep多久
    var tempDelay time.Duration // how long to sleep on accept failure

    srv.trackListener(l, true)
    defer srv.trackListener(l, false)

    // 主goroutine
    baseCtx := context.Background() // base is always background, per Issue 16220
    // 绑定参数
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())

    // accept request
    for {
        // 端口是否可读
        rw, e := l.Accept()
        if e != nil { //不可读或者端口异常、服务异常
            select {
            case <-srv.getDoneChan(): // 如果 http 服务关闭,直接退出程序
                return ErrServerClosed
            default:
            }
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 { // 如果tempDelay == 0 
                    tempDelay = 5 * time.Millisecond // 则 tempDelay == 5ms
                } else {
                    tempDelay *= 2 // 否则 tempDelay * 2
                }
                // tempDelay限定最大1s
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        // 可读=>tempDelay sleep 时间归0
        tempDelay = 0
        // 读取http数据
        c := srv.newConn(rw)
        // 标示请求为新建状态StateNew,除此之外还有:StateActive、StateIdle、StateHijacked、StateClosed
        c.setState(c.rwc, StateNew) // before Serve can return
        // goroutine处理子context
        go c.serve(ctx)
    }
}
// conn中有
func (srv *Server) newConn(rwc net.Conn) *conn {
    c := &conn{
        server: srv,
        rwc:    rwc, //conn,涉及文件描述符操作:可参考 net/fd_unix.go
    }
    return c
}

handler conn

// 接上,子context绑定了一些参数,同时在子context中获取http请求相关信息,例如ip、port、urlpath、body等
func (c *conn) serve(ctx context.Context) {
    // get remote addr
    c.remoteAddr = c.rwc.RemoteAddr().String()

    ... // 处理异常

    // 如果是https请求
    if tlsConn, ok := c.rwc.(*tls.Conn); ok {
        // ... 设置读超时
        // ... 设置写超时
        // ssl/tls握手失败,参考链接:http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
        c.tlsState = new(tls.ConnectionState)
        *c.tlsState = tlsConn.ConnectionState()
        ... // 没理解这段校验代码,跳过
    }

    // HTTP/1.x from here on.

    // 派生可cancel的context,即父context若完成或超时,子context也会cancel,防止资源泄漏
    ctx, cancelCtx := context.WithCancel(ctx)
    c.cancelCtx = cancelCtx
    defer cancelCtx()

    // 获取reader和writer
    c.r = &connReader{conn: c}
    c.bufr = newBufioReader(c.r)
    c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

    for {
        // 组装response对象,其中包括request对象
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // 修改当前context状态为活动中
            c.setState(c.rwc, StateActive)
        }
        if err != nil {
            ...
            // 组装过程出现问题,输出一些内容,然后直接退出
            return
        }

        // 对req进行校验,以及包装其body,方便代码复用获取body
        // Expect 100 Continue support
        req := w.req
        if req.expectsContinue() {
            if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
                // Wrap the Body reader with one that replies on the connection
                req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
            }
        } else if req.Header.get("Expect") != "" {
            ... // 这个校验没理解
            return
        }

        // curReq atomic.Value
        // 线程安全,conn关联response
        c.curReq.Store(w)

        // 异步读取body,通过sync.Cond 阻塞,直到完成读取唤醒阻塞
        if requestBodyRemains(req.Body) {
            registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
        } else {
            if w.conn.bufr.Buffered() > 0 {
                w.conn.r.closeNotifyFromPipelinedRequest()
            }
            w.conn.r.startBackgroundRead()
        }

        // 处理http
        serverHandler{c.server}.ServeHTTP(w, w.req)

        // 结束请求
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()

        // 是否reuse
        if !w.shouldReuseConnection() {
            if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                c.closeWriteAndWait()
            }
            return
        }
        c.setState(c.rwc, StateIdle)
        c.curReq.Store((*response)(nil))

        if !w.conn.server.doKeepAlives() {
            // We're in shutdown mode. We might've replied
            // to the user without "Connection: close" and
            // they might think they can send another
            // request, but such is life with HTTP/1.1.
            return
        }

        if d := c.server.idleTimeout(); d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
            if _, err := c.bufr.Peek(4); err != nil {
                return
            }
        }
        c.rwc.SetReadDeadline(time.Time{})
    }
}

// goroutine 读取
func (cr *connReader) startBackgroundRead() {
    cr.lock()
    defer cr.unlock()
    ...
    cr.inRead = true
    cr.conn.rwc.SetReadDeadline(time.Time{})
    go cr.backgroundRead()
}

// sync.Cond 逻辑处理
func (cr *connReader) backgroundRead() {
    n, err := cr.conn.rwc.Read(cr.byteBuf[:])
    cr.lock()
    if n == 1 {
        cr.hasByte = true
        // We were at EOF already (since we wouldn't be in a
        // background read otherwise), so this is a pipelined
        // HTTP request.
        cr.closeNotifyFromPipelinedRequest()
    }
    if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() {
        // Ignore this error. It's the expected error from
        // another goroutine calling abortPendingRead.
    } else if err != nil {
        cr.handleReadError(err)
    }
    cr.aborted = false
    cr.inRead = false
    cr.unlock()
    // 唤醒阻塞
    cr.cond.Broadcast()
}

处理http请求

// 是个interface
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// gin 实现了这个接口,net/http调用跳至此
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context) //reuse
    c.writermem.reset(w)
    c.Request = req
    c.reset() //reset

    engine.handleHTTPRequest(c)
    engine.pool.Put(c) //reuse
}

// 大体逻辑
func (engine *Engine) handleHTTPRequest(context *Context) {
    httpMethod := context.Request.Method
    path := context.Request.URL.Path
    unescape := false
    if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
        path = context.Request.URL.RawPath
        unescape = engine.UnescapePathValues
    }

    // 根据urlpath从字典树结构取出相对应的处理handler数组
    // 忽略寻找路由相关逻辑
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method == httpMethod {
            root := t[i].root
            // 找到对于的handler数组
            handlers, params, tsr := root.getValue(path, context.Params, unescape)
            if handlers != nil {
                context.handlers = handlers
                context.Params = params
                context.Next()
                // 写resp header
                context.writermem.WriteHeaderNow()
                return
            }
            ...
        }
    }

    ...
    // 404
}

// next遍历handler方法数组,然后调用
func (c *Context) Next() {
    c.index++
    s := int8(len(c.handlers))
    for ; c.index < s; c.index++ {
        c.handlers[c.index](c)
    }
}

// gin context 定义,并非context
type Context struct {
    writermem responseWriter
    Request   *http.Request
    Writer    ResponseWriter

    Params   Params
    handlers HandlersChain // handler 数组
    index    int8 // 当前handler index

    engine *Engine

    // c.Set
    Keys map[string]interface{}

    ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值