• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

gin框架原理

武飞扬头像
qq_37280513
帮助1

1. gin框架概述

地址:https://github.com/gin-gonic/gin 

gin作为web框架,在社区中拥有较高的活跃度,通过使用前缀树结构存储,减少内存消耗,同时加快了路由查询速度,并且在基本功能的基础上提供了各种middleware,可以在访问对应的url时回调对应的handler

也给大家推荐一个go技术选型参考

组件 主选 备选

选型主要考虑

对比

Web框架 Gin[GitHub - gin-gonic/gin: Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.] Echo、Fiber 社区活跃度/成熟度/易用性/性能

Gin/Echo: 社区活跃度较高、性能较高

Fiber: 基于FastHttp,性能很高

Redis go-redis[GitHub - go-redis/redis: Type-safe Redis client for Golang] redigo 社区活跃度/性能/功能

go-redis: 社区活跃度较高、性能较高、集群支持好

redigo: 社区活跃度较高

SQL gendry[GitHub - didi/gendry: a golang library for sql builder] Gorm、Xorm、sqlx 性能/可维护性/功能

gendry: sql builder简单封装,易维护

Gorm/Xorm: 功能强大,社区活跃度高

sqlx: sql builder封装,社区活跃度高

日志 zap[GitHub - uber-go/zap: Blazing fast, structured, leveled logging in Go.] logrus、seelog 性能/功能

zap: 性能较高,结构化日志首选

logrus: 性能适中,格式定义灵活

seelog: 性能一般,简单易用

配置 go-toml[GitHub - pelletier/go-toml: Go library for the TOML file format] viper 易用性/功能

go-toml: toml格式处理,简单易用

viper: 支持多种格式配置、功能强大、依赖较复杂

2. 框架基本功能详解

2.1 一个简单的demo

  1.  
    import (
  2.  
    "fmt"
  3.  
    "github.com/gin-gonic/gin"
  4.  
    "os"
  5.  
    )
  6.  
     
  7.  
    func main() {
  8.  
    //r := gin.Default()
  9.  
    r := gin.New()
  10.  
     
  11.  
    //use must before register route
  12.  
    //注册gin自带中间件
  13.  
    r.Use(gin.Logger())
  14.  
    r.Use(gin.Recovery())
  15.  
     
  16.  
    //注册一个路由和对应的handler
  17.  
    r.GET("/test", func(c *gin.Context) {
  18.  
    c.JSON(200, gin.H{
  19.  
    "message": "hello",
  20.  
    })
  21.  
    })
  22.  
     
  23.  
    //在/test2的路由组中注册中间件,不影响/test
  24.  
    test := r.Group("/test2")
  25.  
    test.Use(MyLogger)
  26.  
    test.Use(MyLogger2)
  27.  
    test.GET("/hello", func(c *gin.Context) {
  28.  
    c.JSON(200, gin.H{
  29.  
    "message": "hello gin",
  30.  
    })
  31.  
    })
  32.  
     
  33.  
    r.Run("127.0.0.1:8899")
  34.  
    }
  35.  
     
  36.  
    func MyLogger(c *gin.Context) {
  37.  
    fmt.Fprintf(os.Stdout, "%s|%s mylogger attach", c.Request.Method, c.Request.URL)
  38.  
    }
  39.  
     
  40.  
    func MyLogger2(c *gin.Context) {
  41.  
    fmt.Fprintf(os.Stdout, "%s|%s mylogger222222 attach", c.Request.Method, c.Request.URL)
  42.  
    }
学新通

这个demo包含了gin框架的一些常用用法,包括自己实现一个简单的middleware,启动后,通过访问localhost:8899/test和localhost:8899/test2/hello可以看到对应的返回,并且可以在控制台看到注册的middleware被调用

学新通

可以看到gin不仅提供了middleware功能,还提供了根据url path注册不同的middleware功能,middleware可以是全局的也可以只针对某个path

2.2 gin.Use

  1.  
    // Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
  2.  
    // included in the handlers chain for every single request. Even 404, 405, static files...
  3.  
    // For example, this is the right place for a logger or error management middleware.
  4.  
    func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
  5.  
    engine.RouterGroup.Use(middleware...)
  6.  
    engine.rebuild404Handlers()
  7.  
    engine.rebuild405Handlers()
  8.  
    return engine
  9.  
    }
  10.  
     
  11.  
     
  12.  
    // Use adds middleware to the group, see example code in GitHub.
  13.  
    func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
  14.  
    group.Handlers = append(group.Handlers, middleware...)
  15.  
    return group.returnObj()
  16.  
    }
  17.  
     
  18.  
    // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
  19.  
    // Create an instance of Engine, by using New() or Default()
  20.  
    type Engine struct {
  21.  
    RouterGroup
  22.  
    ......
  23.  
    }
  24.  
     
  25.  
    // RouterGroup is used internally to configure router, a RouterGroup is associated with
  26.  
    // a prefix and an array of handlers (middleware).
  27.  
    type RouterGroup struct {
  28.  
    Handlers HandlersChain
  29.  
    basePath string
  30.  
    engine *Engine
  31.  
    root bool
  32.  
    }
学新通

engine就是gin.New()或者gin.Default()创建出来的全局实例,其中比较重要的属性之一就是RouterGroup,路由组中包括了注册路由需要的基本信息

Use注册中间件流程:

1. 先看透本质,一个middleware其实就是一个handlerFunc,和注册到path上的api方法签名相同

2. 通过路由组进行注册,将中间件放到RouterGroup路由组的handlers切片中

3. 返回对应的RouterGroup,所以Use函数还可以采用链式调用:

  1.  
    test.Use(MyLogger).Use(MyLogger2)
  2.  
    test.Use(MyLogger, MyLogger2)

2.3 gin.Get

  1.  
    // GET is a shortcut for router.Handle("GET", path, handle).
  2.  
    func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
  3.  
    return group.handle(http.MethodGet, relativePath, handlers)
  4.  
    }
  5.  
     
  6.  
     
  7.  
    func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
  8.  
    absolutePath := group.calculateAbsolutePath(relativePath)
  9.  
    handlers = group.combineHandlers(handlers)
  10.  
    group.engine.addRoute(httpMethod, absolutePath, handlers)
  11.  
    return group.returnObj()
  12.  
    }
  13.  
     
  14.  
    func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
  15.  
    finalSize := len(group.Handlers) len(handlers)
  16.  
    assert1(finalSize < int(abortIndex), "too many handlers")
  17.  
    mergedHandlers := make(HandlersChain, finalSize)
  18.  
    copy(mergedHandlers, group.Handlers)
  19.  
    copy(mergedHandlers[len(group.Handlers):], handlers)
  20.  
    return mergedHandlers
  21.  
    }
  22.  
     
  23.  
    func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  24.  
    ......
  25.  
     
  26.  
    root := engine.trees.get(method)
  27.  
    if root == nil {
  28.  
    root = new(node)
  29.  
    root.fullPath = "/"
  30.  
    engine.trees = append(engine.trees, methodTree{method: method, root: root})
  31.  
    }
  32.  
    root.addRoute(path, handlers)
  33.  
     
  34.  
    ......
  35.  
    }
学新通

从2.2中可以知道engine使用了组合的特性,将RouterGroup组合使用,所以RouterGroup实现的方法,engine也可以调用

Get、DELETE、POST等方法都调用了handle

handle注册api主要流程:

1. 通过combineHandlers方法,将之前在RouterGroup中的中间件和对应的api方法进行合并,并且api方法放到后面,关系到之后的调用顺序,可以从这里看出,先注册的中间件先调用,然后才到api的handler

2. 从前缀树中找到get对应的树,并把path和合并后的handlers放到树中(前缀树的代码会在后续的文章中为大家详解)

3. 这里也可以理解,handler最开始都是放到RouterGroup的切片中,并不会生效,只有通过handle方法,将合并后的handlers放到前缀树中才算生效,所以在Get、Post等方法后使用use是不符合预期的

2.4 Engine.Run

  1.  
    // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
  2.  
    // It is a shortcut for http.ListenAndServe(addr, router)
  3.  
    // Note: this method will block the calling goroutine indefinitely unless an error happens.
  4.  
    func (engine *Engine) Run(addr ...string) (err error) {
  5.  
    ......
  6.  
     
  7.  
    err = http.ListenAndServe(address, engine.Handler())
  8.  
    return
  9.  
    }
  10.  
     
  11.  
    func (engine *Engine) Handler() http.Handler {
  12.  
    if !engine.UseH2C {
  13.  
    return engine
  14.  
    }
  15.  
     
  16.  
    h2s := &http2.Server{}
  17.  
    return h2c.NewHandler(engine, h2s)
  18.  
    }
  19.  
     
  20.  
    // ServeHTTP conforms to the http.Handler interface.
  21.  
    func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  22.  
    c := engine.pool.Get().(*Context)
  23.  
    c.writermem.reset(w)
  24.  
    c.Request = req
  25.  
    c.reset()
  26.  
     
  27.  
    engine.handleHTTPRequest(c)
  28.  
     
  29.  
    engine.pool.Put(c)
  30.  
    }
  31.  
     
  32.  
    func (engine *Engine) handleHTTPRequest(c *Context) {
  33.  
    // Find route in tree
  34.  
    value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
  35.  
    if value.params != nil {
  36.  
    c.Params = *value.params
  37.  
    }
  38.  
    if value.handlers != nil {
  39.  
    c.handlers = value.handlers
  40.  
    c.fullPath = value.fullPath
  41.  
    c.Next()
  42.  
    c.writermem.WriteHeaderNow()
  43.  
    return
  44.  
    }
  45.  
    }
  46.  
     
  47.  
    // Next should be used only inside middleware.
  48.  
    // It executes the pending handlers in the chain inside the calling handler.
  49.  
    // See example in GitHub.
  50.  
    func (c *Context) Next() {
  51.  
    c.index
  52.  
    for c.index < int8(len(c.handlers)) {
  53.  
    c.handlers[c.index](c)
  54.  
    c.index
  55.  
    }
  56.  
    }
学新通

 Run主要流程:

1. 调用golang原生http库,指定一个http.handler,开启监听

2. engine.handler是gin为了支持H2C后来支持的,原来的代码直接传入的是engine,H2C需要了解的可以自行百度,但是一般是不推荐用的,还是推荐用golang默认支持的H2

3. engine作为一个handler实现了ServeHTTP方法,当访问对应的api时,会走到该方法中

4. serveHTTP主要调用的handleHTTPRequest,其他步骤是通过sync.Pool来操作context,用于减少context的内存分配和初始化次数来增加效率减少GC

5. handleHTTPRequest通过访问的httpMethod和uri,getValue找到前缀树中对应的节点,返回的nodeValue对象就是2.3中添加到树中的节点,其中保存了中间件和api对应的handler

6. 通过Next()函数依次调用handler

3. 最后

还有很多功能未在本文中涉及,但是基本的思想和功能已经涵盖,如需继续拓展可以到第一章中的gin框架链接中学习

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfhkii
系列文章
更多 icon
同类精品
更多 icon
继续加载