zoukankan      html  css  js  c++  java
  • Gin框架的工作过程

    1. 使用

      package main
      
      import "github.com/gin-gonic/gin"
      
      func main() {
        // 得到 engine 对象,里面含有 RouterGroup 
          r := gin.Default()
        // 往路由的radix tree里面注册路由及对应的处理方法
          r.GET("/ping", func(c *gin.Context) {
              c.JSON(200, gin.H{
                  "message": "pong",
              })
          })
        //如果run方法中没有填ip:port的话,默认会是 0.0.0.0:8080
          r.Run() // listen and serve on 0.0.0.0:8080
      }
      
    2. 工作流程

      gin框架运行过程

    3. 主要代码解释

      1. 路由注册:

        // 路由组
        func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
        	return &RouterGroup{
        		Handlers: group.combineHandlers(handlers),
        		basePath: group.calculateAbsolutePath(relativePath),
        		engine:   group.engine,
        	}
        }
        
        // POST 方法注册
        func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
        	return group.handle(http.MethodPost, relativePath, handlers)
        }
        
        // GET 方法注册
        func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
        	return group.handle(http.MethodGet, relativePath, handlers)
        }
        
        // 注册路由
        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()
        }
        // 注册路由之前,如果实在某个路由组基础上注册的,会将路由组对应的 handlers 给 加入到 要注册的路由节点
        func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
        	finalSize := len(group.Handlers) + len(handlers)
        	if finalSize >= int(abortIndex) {
        		panic("too many handlers")
        	}
        	mergedHandlers := make(HandlersChain, finalSize)
        	copy(mergedHandlers, group.Handlers)
        	copy(mergedHandlers[len(group.Handlers):], handlers)
        	return mergedHandlers
        }
        
        func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
        	return joinPaths(group.basePath, relativePath)
        }
        
        
        // 真正的注册路由方法的过程
        // 先检测对应的method有没有对应的radix tree,然后再进行注册
        func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
        	root := engine.trees.get(method)
        	if root == nil {
        		root = new(node)
        		root.fullPath = "/"
        		engine.trees = append(engine.trees, methodTree{method: method, root: root})
        	}
        	root.addRoute(path, handlers)
        
        	// Update maxParams
        	if paramsCount := countParams(path); paramsCount > engine.maxParams {
        		engine.maxParams = paramsCount
        	}
        }
        
        //func (n *node) addRoute方法是路由构建过程中最核心的地方--构建radix tree,这个算法会先计算出已有节点与将要插入的节点的共同前缀,然后根据两个节点与共同前缀的长度相比分三种情况(i < len(n.path)、i < len(path)、二者都满足),最终在radix tree中合适的位置插入该结点
        
      2. 服务启动 & 请求处理:

        // 服务启动
        func (engine *Engine) Run(addr ...string) (err error) {
         defer func() { debugPrintError(err) }()
        
         address := resolveAddress(addr)
         debugPrint("Listening and serving HTTP on %s
        ", address)
          // 当请求进来的时候,会走 engine的 ServeHTTP 方法,具体可以看一下 golang 的 http包ListenAndServe方法的解析
         err = http.ListenAndServe(address, engine)
         return
        }
        
        // 相当于流量分发,当有请求到达时,会先经过此方法
        func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        	c := engine.pool.Get().(*Context)
        	c.writermem.reset(w)
        	c.Request = req
        	c.reset()
        
        	engine.handleHTTPRequest(c)
        
        	engine.pool.Put(c)
        }
        
        
        // func (engine *Engine) handleHTTPRequest(c *Context) 方法 从 context对象中获取到请求的method和url,然后从对应method的radix tree中查找对应路径对应的 handlers,将其赋值给context的handlers,调用context.Next 方法去执行 handlers(用户自定义的逻辑代码),自定义的方法中通过调用c.JSON 来给客户端返回json数据
        
        // 执行用户自定义的逻辑代码
        func (c *Context) Next() {
        	c.index++
        	for c.index < int8(len(c.handlers)) {
        		c.handlers[c.index](c)
        		c.index++
        	}
        }
        
    4. gin中路由使用radix tree的好处

      1. 路由中可以使用类似于 :id 动态参数

      2. 路径中的相同部分只会出现在一个结点上,在一定程度上可以减少对内存的占用

      3. 查找效率高(虽然可能达不到map那种 O(1)的时间复杂度,查找复杂度最坏情况下是O(h),h为树高)

      4. 关于radix tree(基数树)的构建及查找我个人还没完全搞明白,感兴趣的朋友可以参考下面两篇文章:路由查找之Radix TreeGin的基数树路由局限及最佳实践

    注:总结不易,如需转载请注明出处:https://www.cnblogs.com/zhuchenglin/p/15094296.html

  • 相关阅读:
    delphi 属性 参数 新注释
    delphi query阻塞执行 长时间执行sql的解决办法
    FDLocalSQL
    C# Webservice
    vmware的centos 6虚拟机如何共享文件夹?
    tomcat如何配置启动时自动部署webapps下的war包
    tomcat如何配置context的docBase
    windows本地启动tomcat闪退
    jfinal 字节流存储文件以及解、压缩工具类
    java try catch 异常后还会继续执行吗
  • 原文地址:https://www.cnblogs.com/zhuchenglin/p/15094296.html
Copyright © 2011-2022 走看看