gin源码解读2-揭开gin的神秘面纱
数据如何在gin中流转
func main() {
	gin.SetMode(gin.DebugMode)  // 设置为开发模式
	router := gin.Default()
	_ = router.SetTrustedProxies([]string{"172.19.10.13"})  // 设置信任代理
	router.GET("/ping", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{"message": "pong"})
		fmt.Println(context.ClientIP())
	})
	_ = router.Run(":8000")
}
这段代码的大概流程:
1.r := gin.Default()初始化了相关的参数 2./ping将路由及处理handler注册到路由树中 3.启动服务
r.Run()其实调用的是err = http.ListenAndServe(address, engine), 结合上一篇文章可以看出来, gin其实利用了net/http的处理过程
ServeHTTP的作用
上一篇文章有提到DefaultServeMux, 其实DefaultServeMux实现了ServeHTTP(ResponseWriter, *Request), 在request执行到server.go的serverHandler{c.server}.ServeHTTP(w, w.req)这一行的时候, 从DefaultServeMux取到了相关路由的处理handler.
因此, gin框架的Engine最重要的函数就是func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request). Engine实现了Handler(server.go#L84-86), 让net/http请求数据最终流回到gin中, 从gin的route tree中取到相关的中间件及handler, 来处理客户端的request
Engine
在整个gin框架中最重要的一个struct就是Engine, 它包含路由, 中间件, 相关配置信息等. Engine的代码主要就在gin.go中
Engine中比较重要的几个属性, 其他的属性暂时全部省略掉
type Engine struct {
    RouterGroup // 路由
    pool             sync.Pool  // context pool
    trees            methodTrees // 路由树
    // html template及其他相关属性先暂时忽略
}
Engine有几个比较主要的函数:
New() Default()
func New() *Engine {
    // ...
    engine := &Engine{
        RouterGroup: RouterGroup{
            Handlers: nil,
            basePath: "/",
            root:     true,
        },
        // ...
        trees: make(methodTrees, 0, 9),
    }
    engine.RouterGroup.engine = engine
    engine.pool.New = func() interface{} {
        return engine.allocateContext()
    }
    return engine
}
New()主要干的事情:
1.初始化了Engine 2.将RouterGroup的Handlers(数组)设置成nil, basePath设置成/ 3.为了使用方便, RouteGroup里面也有一个Engine指针, 这里将刚刚初始化的engine赋值给了RouterGroup的engine指针 4.为了防止频繁的context GC造成效率的降低, 在Engine里使用了sync.Pool, 专门存储gin的Context
func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}
Default()跟New()几乎一模一样, 就是调用了gin内置的Logger(), Recovery()中间件.
Use()
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...)
    engine.rebuild404Handlers()
    engine.rebuild405Handlers()
    return engine
}
Use()就是gin的引入中间件的入口了. 仔细分析这个函数, 不难发现Use()其实是在给RouteGroup引入中间件的. 具体是如何让中间件在RouteGroup上起到作用的, 等说到RouteGroup再具体说.
engine.rebuild404Handlers()
engine.rebuild405Handlers()
这两句函数其实在这里没有任何用处. 我感觉这里是给gin的测试代码用的. 我们在使用gin的时候, 要想在404, 405添加处理过程, 可以通过NoRoute(), NoMethod()来处理.
addRoute()
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})
    }
    root.addRoute(path, handlers)
}
这段代码就是利用method, path, 将handlers注册到engine的trees中. 注意这里为什么是HandlersChain呢, 可以简单说一下, 就是将中间件和处理函数都注册到method, path的tree中了.
Run()系列函数
Run, RunTLS, RunUnix, RunFd 这些函数其实都是最终在调用net/http的http服务.
ServeHTTP
这个函数相当重要了, 主要有这个函数的存在, 才能将请求转到gin中, 使用gin的相关函数处理request请求.
...
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
    if t[i].method != httpMethod {
        continue
    }
    root := t[i].root
    handlers, params, tsr := root.getValue(path, c.Params, unescape)
    if handlers != nil {
        c.handlers = handlers
        c.Params = params
        c.Next()
        c.writermem.WriteHeaderNow()
        return
    }
    ...
}
利用request中的path, 从Engine的trees中获取已经注册的handler
func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}
在Next()执行handler的操作. 其实也就是下面的函数
func(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "pong",
    })
}
如果在trees中没有找到对应的路由, 则会执行serveError函数, 也就是404相关的.
gin源码解读2-揭开gin的神秘面纱的更多相关文章
- netty源码分析之揭开reactor线程的面纱(二)
		如果你对netty的reactor线程不了解,建议先看下上一篇文章netty源码分析之揭开reactor线程的面纱(一),这里再把reactor中的三个步骤的图贴一下 reactor线程 我们已经了解 ... 
- gin源码解读1-net/http的大概流程
		gin框架预览 router.Run()的源码: func (engine *Engine) Run(addr ...string) (err error) { defer func() { debu ... 
- gin源码解读3-gin牛逼的context
		Gin封装的最好的地方就是context和对response的处理. github的README的介绍,基本就是对这两个东西的解释. 本篇文章主要解释context的使用方法, 以及其设计原理 为什么 ... 
- netty源码分析之揭开reactor线程的面纱(一)
		netty最核心的就是reactor线程,对应项目中使用广泛的NioEventLoop,那么NioEventLoop里面到底在干些什么事?netty是如何保证事件循环的高效轮询和任务的及时执行?又是如 ... 
- gin源码剖析
		介绍 Gin 是一个 Golang 写的 web 框架,具有高性能的优点,基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务.官方地址:https:// ... 
- gin 源码阅读(1) - gin 与 net/http 的关系
		gin 是目前 Go 里面使用最广泛的框架之一了,弄清楚 gin 框架的原理,有助于我们更好的使用 gin. 这个系列 gin 源码阅读会逐步讲明白 gin 的原理. gin 概览 想弄清楚 gin, ... 
- gin 源码阅读(2) - http请求是如何流入gin的?
		推荐阅读: gin 源码阅读(1) - gin 与 net/http 的关系 本篇文章是 gin 源码分析系列的第二篇,这篇文章我们主要弄清一个问题:一个请求通过 net/http 的 socket ... 
- gin 源码阅读(5) - 灵活的返回值处理
		gin 源码阅读系列文章列表: gin 源码阅读(1) - gin 与 net/http 的关系 gin 源码阅读(2) - http请求是如何流入gin的? gin 源码阅读(3) - gin 路由 ... 
- Prism 源码解读1-Bootstrapper和Region的创建
		介绍 之前也研究过Prism框架但是一直没有深入理解,现在项目上想把一个Winform的桌面应用程序改造成WPF程序,同时我希望程序是可测试可维护架构良好的,Prism的这些设计理念正好符合我的需求, ... 
随机推荐
- Centos使用kafka自带zookeeper安装kafka
			首先要安装jdk环境 可以参考:https://www.cnblogs.com/pxblog/p/10512886.html 下载kafka 地址:http://kafka.apache.org/do ... 
- JAVA中SpringMVC获取bean方法,在工具类(utils)注入service
			有时候我们会出现无法用注解 @Autowired 注入bean的情况,这个时候可以 通过contextLoader获取 WebApplicationContext ctx = ContextLoade ... 
- Flutter学习(9)——Flutter插件实现(Flutter调用Android原生
			原文地址: Flutter学习(9)--Flutter插件实现(Flutter调用Android原生) | Stars-One的杂货小窝 最近需要给一个Flutter项目加个apk完整性检测,需要去拿 ... 
- 【LeetCode】 258. Add Digits 解题报告(Java & Python)
			作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:递归 方法二:减1模9 方法三:直接模9 日 ... 
- 【九度OJ】题目1208:10进制 VS 2进制 解题报告
			[九度OJ]题目1208:10进制 VS 2进制 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1208 题目描述: 对于一 ... 
- 【LeetCode】467. Unique Substrings in Wraparound String 解题报告(Python)
			作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/unique-s ... 
- 【LeetCode】384. Shuffle an Array 解题报告(Python & C++)
			作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 库函数 Fisher–Yates 洗牌 水塘抽样 日 ... 
- 教学日志:javaSE-面向对象2
			一.局部变量和成员变量 package class4.oop1; /** * @Auther: Yu Panpan * @Date: 2021/12/10 - 12 - 10 - 14:47 * @D ... 
- 数据结构作业——P53页算法设计题(7):原地逆转链表
			一. 题目描述: 设计一个算法,将链表中所有结点的链接方向"原地"逆转,即要求仅利用原表的存储空间,换句话说,要求算法的空间复杂度为O(1). 二.算法设计 #include< ... 
- MacOS使用IDEA+Maven+Scala+Spark进行本地调试
			参考:spark开发环境搭建(基于idea 和maven) 安装JDK 从这里下载Java 8的JDK 设置JAVA_HOME环境变量,在Mac上它大概会是/Library/Java/JavaVirt ... 
