Gin源码分析(一)官方demo分析

来源:互联网 发布:淘宝店铺信誉怎么算 编辑:程序博客网 时间:2024/06/06 01:43

目录:

  • 目录
    • 简介
    • 官方demo
    • 1 ginDefault
      • 11 看到New函数
      • 12 debugPrintWARNINGNew
      • 13 新建engine
      • 14 分配Context
    • 2 engineUse
      • 21 Logger
      • 22 recovery
      • 23 engineUse
    • 3 ginGET
    • 3 routerrun

简介

Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。

最近一直在学习Go语言,迅速扫了一遍《Go语言圣经》,觉得只有理论没有实践可不行,因此,很喜欢简洁的gin,就先来看这个吧。

官方demo

首先看官方demo

package mainimport "github.com/gin-gonic/gin"func main() {    r := gin.Default()    r.GET("/ping", func(c *gin.Context) {        c.JSON(200, gin.H{            "message": "pong",        })    })    r.Run() // listen and serve on 0.0.0.0:8080}

1.1 gin.Default

// Default returns an Engine instance with the Logger and Recovery middleware already attached.func Default() *Engine {    engine := New()    engine.Use(Logger(), Recovery())    return engine}

这段代码主要完成了以下内容:

  1. 新建一个engine
  2. 使用默认的log、recovery中间层

1.1.1 看到New()函数

func New() *Engine {    debugPrintWARNINGNew()    engine := &Engine{        RouterGroup: RouterGroup{            Handlers: nil,            basePath: "/",            root:     true,        },        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    engine.pool.New = func() interface{} {        return engine.allocateContext()    }    return engine}

New方法主要生成一个Engine的实例,并进行配置。

1.1.2 debugPrintWARNINGNew()

我们每次启动gin服务器,如果不加

gin.SetMode(gin.ReleaseMode)

这一段,就会输出一段提示

[WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env:   export GIN_MODE=release - using code:  gin.SetMode(gin.ReleaseMode)

1.1.3 新建engine

type Engine struct {    RouterGroup    delims           render.Delims    secureJsonPrefix string    HTMLRender       render.HTMLRender    FuncMap          template.FuncMap    allNoRoute       HandlersChain    allNoMethod      HandlersChain    noRoute          HandlersChain    noMethod         HandlersChain    pool             sync.Pool    trees            methodTrees

engine是框架实例,主要包括:

  1. muxer 我也不知道是干嘛的,看面看到了再回头改吧
  2. 中间层 中间处理连接的过程,比如 logger、recovery
  3. 其他设置

1.1.4 分配Context

type Context struct {    writermem responseWriter    Request   *http.Request    Writer    ResponseWriter    Params   Params    handlers HandlersChain    index    int8    engine *Engine    // 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}

context是gin的一个重要组成部分。用来在中间层传递数据流。

1.2 engine.Use

engine.Use(Logger(), Recovery())

先看到Logger()

1.2.1 Logger()

这里定义了一个log中间层,在连接处理的过程中生成日志并输出

上函数:

func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {    isTerm := true    if w, ok := out.(*os.File); !ok ||        (os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) ||        disableColor {        isTerm = false    }    var skip map[string]struct{}    if length := len(notlogged); length > 0 {        skip = make(map[string]struct{}, length)        for _, path := range notlogged {            skip[path] = struct{}{}        }    }    return func(c *Context) {        // Start timer        start := time.Now()        path := c.Request.URL.Path        raw := c.Request.URL.RawQuery        // Process request        c.Next()        // Log only when path is not being skipped        if _, ok := skip[path]; !ok {            // Stop timer            end := time.Now()            latency := end.Sub(start)            clientIP := c.ClientIP()            method := c.Request.Method            statusCode := c.Writer.Status()            var statusColor, methodColor, resetColor string            if isTerm {                statusColor = colorForStatus(statusCode)                methodColor = colorForMethod(method)                resetColor = reset            }            comment := c.Errors.ByType(ErrorTypePrivate).String()            if raw != "" {                path = path + "?" + raw            }            fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",                end.Format("2006/01/02 - 15:04:05"),                statusColor, statusCode, resetColor,                latency,                clientIP,                methodColor, method, resetColor,                path,                comment,            )        }    }}

这部分代码主要做了以下内容:

  1. 判断日志的输出,默认输出到 stdout
  2. 对收到的连接,输出日志,日志的内容包括:请求处理成功的时间戳、http返回状态码、耗时、请求IP、请求方法、请求路径、错误信息

1.2.2 recovery()

// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.func RecoveryWithWriter(out io.Writer) HandlerFunc {    var logger *log.Logger    if out != nil {        logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)    }    return func(c *Context) {        defer func() {            if err := recover(); err != nil {                if logger != nil {                    stack := stack(3)                    httprequest, _ := httputil.DumpRequest(c.Request, false)                    logger.Printf("[Recovery] panic recovered:\n%s\n%s\n%s%s", string(httprequest), err, stack, reset)                }                c.AbortWithStatus(500)            }        }()        c.Next()    }}

这一块主要利用了 Go语言的recovery,实现从错误中回复。

当请求出现错误时,造成程序报错,将在此恢复,输出日志并返回 状态码500

1.2.3 engine.Use()

这里主要应用中间层

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {    engine.RouterGroup.Use(middleware...)    engine.rebuild404Handlers()    engine.rebuild405Handlers()    return engine}

这里在 engine的 RouterGroup中添加了中间层。

404he 405 使用 noRoute 和 noMethod

1.3 gin.GET()

// GET is a shortcut for router.Handle("GET", path, handle).func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {    return group.handle("GET", relativePath, handlers)}

这里仔细看,这里的 方法 接受者 是 RouterGroup 并不是 Default 返回的 Engine。

再去看 Engine的定义

type Engine struct {    RouterGroup    delims           render.Delims    secureJsonPrefix string    HTMLRender       render.HTMLRender    FuncMap          template.FuncMap    allNoRoute       HandlersChain    allNoMethod      HandlersChain    noRoute          HandlersChain    noMethod         HandlersChain    pool             sync.Pool    trees            methodTrees    RedirectTrailingSlash bool    RedirectFixedPath bool    HandleMethodNotAllowed bool    ForwardedByClientIP    bool    AppEngine bool    UseRawPath bool    UnescapePathValues bool    MaxMultipartMemory int64}

这里使用到了 struct 匿名成员的特性。RouterGroup定义为匿名成员,也就是Engine继承了RouterGroup。

因此Engine可以调用RouterGroup的方法。

再看到group.handle 方法,

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

主要完成了以下内容:

  1. 计算出绝对路径
  2. 将handlers加入HandlersChain
  3. 添加路由,这里有一颗方法树,在方法树中添加 该路由

1.3 router.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)    err = http.ListenAndServe(address, engine)    return}

这里首先判断端口,默认为8080端口,

这里调用了 go语言的 net包中的http的 ListenAndServe

至此,官方的 启动demo分析完成。

这只是我对于gin的源码分析的第一个版本,还有很多不足,后续会在此基础上进行完善,如有错误欢迎指出。

原创粉丝点击