gin源码笔记1
前言
主要是通过gin的源码,学习一些golang编程的常见技巧。同时积累标准库的用法。
options模式
一种设置结构体参数的方法,用于设置默认值,但用户可以传入option来修改默认值,具备可扩展性。
- gin初始化时会传入opts1 
 2
 3
 4
 5
 6
 7func New(opts ...OptionFunc) *Engine { 
 engine := &Engine{
 // 默认参数
 ...
 }
 return engine.With(opts...)
 }
- options应用1 
 2
 3
 4
 5
 6
 7func (engine *Engine) With(opts ...OptionFunc) *Engine { 
 for _, opt := range opts {
 opt(engine)
 }
 return engine
 }
- 如可以写一个OptionFunc并加载1 
 2
 3
 4
 5
 6
 7func WithForwardedByClientIP() gin.OptionFunc { 
 return func(engine *gin.Engine) {
 engine.ForwardedByClientIP = true
 }
 }
 r := gin.Default(WithForwardedByClientIP())
中间件扩展原理
- 插入中间件,实际上将中间件对应的函数闭包 - HanlerFunc存到路由切片里- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { 
 engine.RouterGroup.Use(middleware...)
 ...
 return engine
 }
 // Use adds middleware to the group, see example code in GitHub.
 func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
 group.Handlers = append(group.Handlers, middleware...)
 return group.returnObj()
 }
- 以默认两个中间件为例,可以看到中间件 - HandlerFunc会操作- gin.Context变量。调用- c.Next()前为中间件前处理,调用后为请求完毕后处理。如日志中间件会记录调用耗时并打印。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42- engine.Use(Logger(), Recovery()) 
 func Logger() HandlerFunc {
 return LoggerWithConfig(LoggerConfig{})
 }
 // LoggerWithConfig instance a Logger middleware with config.
 func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
 ...
 return func(c *Context) {
 // Start timer
 start := time.Now()
 path := c.Request.URL.Path
 raw := c.Request.URL.RawQuery
 // Process request
 c.Next()
 ...
 param := LogFormatterParams{
 Request: c.Request,
 isTerm: isTerm,
 Keys: c.Keys,
 }
 // Stop timer
 param.TimeStamp = time.Now()
 param.Latency = param.TimeStamp.Sub(start)
 param.ClientIP = c.ClientIP()
 param.Method = c.Request.Method
 param.StatusCode = c.Writer.Status()
 param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
 param.BodySize = c.Writer.Size()
 if raw != "" {
 path = path + "?" + raw
 }
 param.Path = path
 fmt.Fprint(out, formatter(param))
 }
 }
- Next()函数会执行后面的中间件- 1 
 2
 3
 4
 5
 6
 7- func (c *Context) Next() { 
 c.index++
 for c.index < int8(len(c.handlers)) {
 c.handlers[c.index](c)
 c.index++
 }
 }
- 假如绑定了一个ping,当一个ping到达gin时,处理过程类似洋葱模型。跟深度优先遍历算法代码基本一样  - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25- r.GET("/ping", func(c *gin.Context) { 
 c.JSON(http.StatusOK, gin.H{
 "message": "pong",
 })
 })
 r.Run()
 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()
 }
 // 这里将所有handler汇总,返回最终的HandlersChain
 func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
 finalSize := len(group.Handlers) + len(handlers)
 assert1(finalSize < int(abortIndex), "too many handlers")
 mergedHandlers := make(HandlersChain, finalSize)
 copy(mergedHandlers, group.Handlers)
 copy(mergedHandlers[len(group.Handlers):], handlers)
 return mergedHandlers
 }
| 1 | func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { | 
- 请求到达,取出method树对应的节点挂着的hanlders,赋值给Context,然后调用Next(),开始像洋葱模式一样执行。
| 1 | // ServeHTTP conforms to the http.Handler interface. | 
| 1 | func (engine *Engine) handleHTTPRequest(c *Context) { | 
标准库函数积累
| 1 | // 测试format是否以 \n 结尾。 | 
对象池sync.Pool
sync.Pool 是 Go 语言标准库中的一个并发安全的内存池,主要用于临时对象的存储和复用,从而减少频繁的内存分配和垃圾回收,提高性能。
sync.Pool 主要用于存储短期使用的对象,特别是在高并发场景下,能够有效地减少内存分配的开销。它的设计适合于那些生命周期短暂的对象,比如在请求处理中使用的临时数据结构。
| 1 | // 分配Context的函数 |