Gin源码解析

学习gin框架源码的优秀设计…

gin框架使用的定制版本的http router。
http router的原理是大量使用公共前缀的树结构,

它基本是一个紧凑的trie terr或者只是一个RadixTree(基数树)

学到的优秀设计:

gin中的Context poll

Poll用于存储哪些被分配了但是没有被使用,而未来有可能会被使用的值,以减少垃圾回收的压力.

在Web应用中,后台处理用户的每条请求都会为当前请求创建一个上下文环境Context,用于存储请求信息及相关信息等.Context满足一个长生命周期的特点,且用户请求也属于并发环境,所以对于线程安全的Poll非常适合用来维护Context的临时对象池.

Gin在结构体Engine中定义了一个poll

1
2
3
4
type Engine struct {
// ... 省略了其他字段
pool sync.Pool
}

初始化engine时定义了poll的New函数:

1
2
3
4
5
6
7
8
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
// allocateContext
func (engine *Engine) allocateContext() *Context {
// 构造新的上下文对象
return &Context{engine: engine}
}

ServerHttp

1
2
3
4
5
6
7
8
9
10
// 从 pool 中获取,并转化为 *Context
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset() // reset

engine.handleHTTPRequest(c)

// 再扔回 pool 中
engine.pool.Put(c)

其他

  1. 基于sync.poll使用对象池poll,从池中获取后对它转化Context,减少每次创建方法的CG和内存申请的频率,使用完之后,再扔回对象池中.

避免对象的引用的干扰,在不影响项目的实际功能的情况下取出对象。

  1. 在设计for循环时的优秀设计:
1
2
3
for i:=0,lens=len(arr);i<lens;i++{

}
  1. 提倡最小执行代码原则:

把应该立即返回的条件写在前面

4.在写完一个结构体后,在结构体下方新建一个接口类型的匿名变量

目的:为了确保结构体实现了这个接口,把错误保留在编译阶段

1
2
type Engine struct{...}
var _ Irouter [匿名变量的类型] = &Engine{}

5.为什么不用map而要用树遍历的方法? map比较slice更占内存

1
2
3
4
5
6
7
8
func (trees methodTrees) get(method string) *node {
for _, tree := range trees {
if tree.method == method {
return tree.root
}
}
return nil
}

6.当已知一个切片的容量时,我可以在初始化的时候一次性把切片的容量申请到位,避免动态申请slice。


Gin源码解析
https://zhyyao.me/2021/05/03/technology/golang/gin_code_analyse/
作者
zhyyao
发布于
2021年5月3日
许可协议