开篇
-
首先gin 框架是在 官方提供的net/http标准包进行的相应封装。
-
那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识。
-
可以参考中文的 文档: https://studygolang.com/pkgdoc
-
可以参考的很多文章视频等:
- gin源码分析流程思维导图:https://www.processon.com/view/link/5f4b70c2079129356ec5cb70#map
- gin源码解读
- 哔哩哔哩视频讲 gin源码: https://www.bilibili.com/video/BV1Br4y1N7DG?spm_id_from=333.999.0.0
参考大佬文章: 两篇gin技术内幕:https://zhuanlan.zhihu.com/p/133208366(重点)参考大佬两篇:深入gin框架内幕 https://zhuanlan.zhihu.com/p/102303084(重点)- https://www.cnblogs.com/yjf512/p/9670990.html
- https://blog.youkuaiyun.com/u012988972/article/details/118991542
- 等等资料和视频(资料文章能找到很多,我也没咋都看,但是有机会可以多看看,融入融入)
另外: 文章源码的上述注释解析,来自机器翻译,可能有不准确的情况。
-
有话讲:
- 有的地方可能是我自己的推敲与判断,整体分析的思路,也是一步步来的。
- 锻炼阅读源码的能力, 如何搞清楚一个框架的定位,以及如何,从那一步开始研究框架和源码,这都是一个学习的过程。
- 开场,会从最基本的入门小例子讲起。
- 阅读优秀的代码,还会发现很多比较优质的代码书写的相关技巧!都会总结一下。比如 for 遍历先初始化 len(t), 保证结构体是否实现相关接口等等。
-
同时还可以参考对应的 官方文档, 有相关项目经验后再来分析源码,这样你就会思考,
我在框架中使用的某些功能,或者逻辑处理流程是如何实现的?这些都很关键。- github上当然有官方的文档介绍: https://github.com/gin-gonic/gin#gin-web-framework, 并且还有相关的 例子,就在examples 目录下。
- 在打开 github 查看源码的时候,可以将链接变为 https://github1s.com, 加个 “1s” , 项目会被打开成 vscode 风格! 方便查看。 很棒~
- 还有相关的中文文档, 访问比较快:https://www.kancloud.cn/shuangdeyu/gin_book/949413
-
引用文章中的一段描述介绍开篇:

开始
1. 从入门教程推出 Engine 对象
-
从最开始的语句来入门教程:
func main() { // 创建一个默认的路由引擎 r := gin.Default() // GET:请求方式;/hello:请求的路径 // 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数 r.GET("/hello", func(c *gin.Context) { // c.JSON:返回JSON格式的数据 c.JSON(200, gin.H{ "message": "Hello world!", }) }) // 启动HTTP服务,默认在0.0.0.0:8080启动服务 r.Run() } -
我们点击进去 Run 函数的源码:
//执行命令将路由器连接到http。服务器并开始侦听和服务HTTP请求。 // http的快捷方式。ListenAndServe (addr,路由器) //注意:这个方法会无限期地阻塞调用goroutine,除非发生错误。 func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() trustedCIDRs, err := engine.prepareTrustedCIDRs() if err != nil { return err } engine.trustedCIDRs = trustedCIDRs address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) err = http.ListenAndServe(address, engine) return }- 其中最核心的语句就是 :
err = http.ListenAndServe(address, engine)-
这就是调用的官方标准包, net/http。 可以去查看对应的用法:

- 它负责监听 tcp请求,并且交给 handler 参数去调用一个 Server函数处理接收到的链接。
- 一般handler 为 nil, 那么默认有一个 DefaultServeMux 对象, 去调用 server函数处理请求。
-
打开 http.ListenAndServe 源码,会发现,它获取的handler参数就是传递给 Server对象的。
// ListenAndServe监听TCP网络地址addr,然后调用 //使用handler处理传入连接的请求。 //已接受的连接配置为启用TCP keep-alive。 // //处理程序通常为nil,在这种情况下使用DefaultServeMux。 // // ListenAndServe总是返回一个非nil错误。 func ListenAndServe(addr string, handler Handler) error { server := &Server{ Addr: addr, Handler: handler} //源码中创建 server服务器对象,将handler传递进去 return server.ListenAndServe() } -
Server函数: server对象的方法,接收每一个链接,并开启 go程,读取请求,调用handler回复请求。

-
默认的 DefaultServeMux
// ServeMux是一个HTTP请求复用器。 //它将每个传入请求的URL与一个已注册的列表进行匹配 // 的模式,并调用处理程序的模式 //最接近URL。 // //模式名称固定,根路径,如"/favicon.ico", //或根子树,如"/images/"(注意后面的斜杠)。 //较长的模式优先于较短的模式,因此 //如果有两个处理程序注册"/images/" //和"/images/thumbnails/",后一个处理器将是 //调用以"/images/thumbnails/"开头的路径 //前将接收任何其他路径的请求 / / /图片/子树。 // //注意,因为以斜杠结尾的模式命名了根子树, //模式"/"匹配所有未被其他注册的路径 //模式,而不仅仅是URL与Path == "/"。 // //如果一个子树已经注册,并且接收到一个命名为 //不带末尾斜杠的子树根,ServeMux重定向它 //请求到子树根(添加末尾的斜杠)。这种行为可以 //被一个单独的注册路径覆盖 //末尾的斜杠。例如,注册“/images/”会导致ServeMux //重定向请求"/images"到"/images",除非"/images"有 //已单独注册。 // //模式可以选择以主机名开头,限制匹配为 //该主机上的url。特定于主机的模式优先 //通用模式,以便处理程序可以注册这两个模式 // "/codesearch"和"codesearch.google.com/"不需要接管 //请求“http://www.google.com/”。 // // ServeMux还负责清理URL请求路径和主机 //头,剥离端口号和重定向任何请求包含。或 / / . .元素或重复的斜杠到等效的、更干净的URL。 type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames } // NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return new(ServeMux) } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = &defaultServeMux var defaultServeMux ServeMux -
而对应的server对象在这
type Server struct { Addr string // 监听的TCP地址,如果为空字符串会使用":http" Handler Handler // 调用的处理器,如为nil会调用http.DefaultServeMux ReadTimeout time.Duration // 请求的读取操作在超时前的最大持续时间 WriteTimeout time.Duration // 回复的写入操作在超时前的最大持续时间 MaxHeaderBytes int // 请求的头域最大长度,如为0则用DefaultMaxHeaderBytes TLSConfig *tls.Config // 可选的TLS配置,用于ListenAndServeTLS方法 // TLSNextProto(可选地)指定一个函数来在一个NPN型协议升级出现时接管TLS连接的所有权。 // 映射的键为商谈的协议名;映射的值为函数,该函数的Handler参数应处理HTTP请求, // 并且初始化Handler.ServeHTTP的*Request参数的TLS和RemoteAddr字段(如果未设置)。 // 连接在函数返回时会自动关闭。 TLSNextProto map[string]func(*Server, *tls.Conn, Handler) // ConnState字段指定一个可选的回调函数,该函数会在一个与客户端的连接改变状态时被调用。 // 参见ConnState类型和相关常数获取细节。 ConnState func(net.Conn, ConnState) // ErrorLog指定一个可选的日志记录器,用于记录接收连接时的错误和处理器不正常的行为。 // 如果本字段为nil,日志会通过log包的标准日志记录器写入os.Stderr。 ErrorLog *log.Logger // 内含隐藏或非导出字段 }
-
- 其中最核心的语句就是 :
-
可以看到, 我们的代码中:
err = http.ListenAndServe(address, engine)-
也就是将 engine 传递进去, 作为 handler 参数,也就是不使用默认的 DefaultServeMux -
engine 是 *Engine 类型。 也就是 我们 gin.Default() 出来的“引擎”(Default 出来的默认带有两个中间件)。 engin 既然可以作为 Handler 类型参数, 那么肯定实现了对应的接口:
//处理程序响应HTTP请求。 // // serverhttp应该向ResponseWriter写入应答头和数据 //返回。返回请求完成的信号;它 // 的ResponseWriter或从 //请求。正文后或与之同时完成 // ServeHTTP电话。 // //根据HTTP客户端软件、HTTP协议版本和 //客户端和Go服务器之间的任何中介,它可能不会 //可以读取请求。身体写完后给 // ResponseWriter。谨慎的处理程序应该读取请求。身体 //首先,然后回复。 // //除了读取主体外,处理程序不应该修改 //请求提供。 // //如果ServeHTTP崩溃,服务器(调用ServeHTTP的人)会假设 //将panic的影响与主动请求隔离。 //它恢复panic,记录堆栈跟踪到服务器错误日志, //关闭网络连接或者发送HTTP/2 // RST_STREAM,取决于HTTP协议。中止处理程序,所以 //客户端看到一个中断的响应,但服务器没有记录 //一个错误,慌乱与值ErrAbortHandler。 type Handler interface { ServeHTTP(ResponseWriter, *Request) }
-
-
总结:
- 调用Run方法, 实际底层是使用 http包的方法, 将 Engine 类型对象作为 handler 参数传递进去(实现了Handler接口),构造 Server对象, 代替默认的 DefaultServeMux 处理请求链接,DefaultServeMux 实现了 相关的 match 等方法(去server.go 搜索ServeMux, 找到它实现的相关方法) 。
2. Engine 对象介绍及handleHTTPRequest接口实现与路由方法树树相关操作
- 认清
Engine 对象及giin框架。- 源码上方的介绍:
Engine 是框架的实例,它包含muxer,中间件和配置设置。- 可以看到是非常重要的! 同时我们也知道了 gin 框架的主要作用, 就是 充当了 muxer 代替 net/http 包里 启动服务器对象的默认 muxer, 同时还提供了 中间件,配置设置等功能
- 源码上方的介绍:
- 我们查看源码,找到Egine 对象: 比较长,注释很清晰,每个功能的作用。
-
参考文章: https://zhuanlan.zhihu.com/p/102303084, Engine 相关常用的方法,在这里都有介绍。一定进去查看下!
- 理解了其实还可以使用 goland编辑器, 鼠标停在gin,Default()。Defalut上。 然后出现返回类型, *Engine, 点击一下,就会出现相关具有的方法 和 内容。(缺少一个maxParams参数,老版本这个参数是在路由树的里定义的)
//Engine是框架的实例,它包含muxer,中间件和配置设置。 //创建一个Engine实例,使用New()或Default() type Engine struct { // 路由组,在实际开发过程中我们通常会使用路由组来组织和管理一些列的路由. 比如: /apis/,/v1/等分组路由 RouterGroup // 开启自动重定向。如果当前路由没有匹配到,但是存在不带/开头的handler就会重定向. 比如: 用户输入/foo/但是存在一个/foo 就会自动重定向到该handler,并且会向客户端返回301或者307状态码(区别在于GET方法和其他方法) RedirectTrailingSlash bool // 如果开启该参数,没有handler注册时,路由会尝试自己去修复当前的请求地址. // 修复流程: // 1.首位多余元素会被删除(../ or //); 2.然后路由会对新的路径进行不区分大小写的查找;3.如果能正常找到对应的handler,路由就会重定向到正确的handler上并返回301或者307.(比如: 用户访问/FOO 和 /..//Foo可能会被重定向到/foo这个路由上) RedirectFixedPath bool // 如果开启该参数,当当前请求不能被路由时,路由会自己去检查其他方法是否被允许.在这种情况下会响应"Method Not Allowed",并返回状态码405; 如果没有其他方法被允许,将会委托给NotFound的handler HandleMethodNotAllowed bool // 是否转发客户端ip ForwardedByClientIP bool // 如果开启将会在请求中增加一个以"X-AppEngine..."开头的header AppEngine bool // 如果开启将会使用url.RawPath去查找参数(默认:false) UseRawPath bool // 如果开启,请求路径将不会被转义. 如果UseRawPath为false,该参数实际上就为true(因为使用的是url.Path) UnescapePathValues bool // maxMemory参数的值(http.Request的ParseMultipartForm调用时的参数) MaxMultipartMemory int64 // 是否删除额外的反斜线(开始时可解析有额外斜线的请求) RemoveExtraSlash bool // 分隔符(render.Delims表示使用HTML渲染的一组左右分隔符,具体可见html/template库) delims render.Delims // 设置在Context.SecureJSON中国的json前缀 secureJsonPrefix string // 返回一个HTMLRender接口(用于渲染HTMLProduction和HTMLDebug两个结构体类型的模板) HTMLRender render.HTMLRender // html/template包中的FuncMap map[string]interface{} ,用来定义从名称到函数的映射 FuncMap template.FuncMap // 以下
-

本文深入解析Gin框架的实现原理,包括HTTP请求处理流程、路由匹配机制、中间件设计及Context对象的应用。通过源码分析,揭示Gin框架高效运作的秘密。
最低0.47元/天 解锁文章
7075

被折叠的 条评论
为什么被折叠?



