这是我参与「第三届青训营 -后端场」笔记创作活动的第四篇笔记
在青训营的学习到达尾声时,我们组开始准备此次大作业的编写,由于组内的同学都是对gin框架了解的稍微多一些,我就再次复习了一遍gin框架,并且写了一些自己对于gin简单的理解,希望对同学们去编写代码时能有所帮助。
gin的源码解析一:
我们新建一个项目,在项目中引入gin框架,新建一个文件,实现基础的gin的实例
首先,我们使用了gin.Default()生成了一个实例,这个实例即 WSGI (小贴士3)应用程序。
既然函数的执行是通过r.Run,那我们可以跟进去看一看gin的源码
在这里我我们看到,Run函数中,对于数据的处理,仍然是http.下的函数进行。因为gin框架就是基于http标准库开发的,其网络通信部分也就是利用http库来完成
ListenAndServe 函数需要接收两个参数,第一个是路径,启动端口,第二个参数是一个接口,这个接口的要求我们点进去后可以看到,他要求必须要实现一个servehttp方法
我们看到,gin框架这里传给ListenAndServe 函数的第二个参数叫engine,是一个Engine指针类型的接收者变量,而Engine就是gin框架比较核心的引擎结构体
也就是说Engine内有实现serverhttp方法的代码
那我们接下来就需要去找到这个结构体实现serverhttp方法的地方
这里引入一个goland查看源代码的方便之处,在结构体声明的左侧会有一个标记
鼠标点击即可找到该结构体实现了那些接口
这里点击第一个我们就会发现,它就找到了我们刚才点出来的Handler接口处
这时左边再次出现标记,这个标记点开展示的实现了该方法/接口的函数/结构体,我们点击对应ServeHTTP的标记,果不其然在期中找到了Engine
再次点击,即可定位到Engine实现ServerHTTP方法的地方
一点进来的第一行代码,我们就看见了一个典型案例 sync.Pool对象池(小贴士4)
c := engine.pool.Get().(*Context)
这里engine是一个可以重用的对象,gin构建了一个对象池,来进行复用engine以减少GC
.GET方法就是在池子中捞出一个engine对象,然后过一个类似类型断言的方式去将之转换为一个Context(gin框架内的结构体)
这里还有一个技巧
我们可以看到,源码在将对象取出来之后,再去做一个reset初始化的操作,【这里的w是http的ResponseWriter 接口(小贴士5)实际上是一个http.response 对象实例】,这个初始化是做在使用它之前这样做可以避免对象的引用与干扰。使用完就直接放回去,要使用之前在初始化
中间的这一步就是处理http请求的,我们点进去查看
在这里有一个个小技巧,for循环的时候,先用一个变量注册为要遍历的变量长度,然后将for的第二个判断条件改为那个变量,这样就可以避免len函数的多次使用
go语言中提倡最小执行原则
要把应该立即判断的、返回的代码写在前面。
在红框内,gin做了一个请求方法的判断,我们注意到t是一个engine.trees变量,那么这个trees是什么呢,我们点上去看一下
在engine结构体下方有一行代码
var _ IRouter = &Engine{}
这是很常用的一种方法,通过匿名接口结构体来确保一个结构体实现了某个接口,这样写的原理是将错误暴露在编译器中,我们写下这一行代码后如果没有实现IRouter接口,那么编译器会报错
回到上文我们发现这个trees是一个请求方法树,点进去
在查找请求方法对应的树的时候,使用了一个for循环去找。因为http的请求方法常用的就那几种,使用map进行关系映射存储会比较占用内存。所以gin框架里直接将方法树用一个切片来承载,请求来到时,通过传入的请求与方法去遍历找到请求方法树,找到则将根节点返回。