内容简介:文章首发于同名公众号,欢迎关注~因为上面就是使用了
文章首发于同名公众号,欢迎关注~
因为 gin 的安装教程已经到处都有了,所以这里省略如何安装, 建议直接去 github 官方地址的 README 中浏览安装步骤,顺便了解 gin 框架的功能。 https://github.com/gin-gonic/gin
最简单的代码
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
上面就是使用了 gin 框架的最简单的 demo ,接下来通过这个 demo 去一步步阅读分析 gin 是如何启动的。
gin.Run()
这里是服务器启动的地方,进去看看这个函数有什么奥秘:
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
除去第一行和第四行打印,是不是有点像 go 原生的 http 库?来看看 http 库的 web 服务简单 demo
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", indexHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
可以看到 gin 框架启动的时候,也是基于 http.ListenAndServe(addr string, handler Handler) 这个方法的,与原生的 http 库的区别就是, ListenAndServe(addr string, handler Handler) 这个方法的第二个参数,传入的是 engine ,我们继续来看看 ListenAndServe 方法的具体代码:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
server.ListenAndServe() 已经是服务监听并启动的方法,所以关键点在 Handler 这里:
// A Handler responds to an HTTP request.
// ...
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
这是 http 包里的源码注释,其实写得很详细了,注释很多,我直接省略了,感兴趣的可以去阅读一下,不出意外,这个 Handler 是一个接口,从 gin.engine 的传入已经可以看出来了吧?注释的意思是这个接口是 处理程序响应HTTP请求 ,可以理解为,实现了这个接口,就可以接收所有的请求并进行处理。
因此 gin 框架的入口就是从这里开始, gin engine 实现了 ServeHTTP ,然后接管所有的请求走 gin 框架的处理方式。细心的读者应该能发现,原生的 http web 服务传入了 nil ,事实上是 http 库也有一个默认的请求处理器,感兴趣的读者可以去仔细阅读研究一下 go 官方团队的请求处理器实现哦~
那我们继续来看下 gin 框架是怎么实现这个 ServeHTTP 方法的:
// gin.go
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context) // 从池里获取资源处理请求
c.writermem.reset(w) // 重置资源的 ResponseWriter
c.Request = req // 赋值请求给 context
c.reset()
engine.handleHTTPRequest(c) // 将资源信息给到 handler 进一步处理
engine.pool.Put(c) // 请求处理结束,将资源放回池子
}
可以看到实现并不难,代码的含义我已经写上了注释,这里的重点是 Context ,但不是这一篇文章的重点,我放在这一系列的后面进行讲解。
我们只需要知道, gin 也是实现了 Handler 接口,所以可以将请求按 gin 的处理方式进行处理,也就是我们可以使用 gin 来做 web 服务框架的起点。
gin.Default()
接下来进去 Default 函数去看具体实现,代码如下:
// gin.go
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault() // 忽略
engine := New()
engine.Use(Logger(), Recovery()) // 暂时忽略
return engine
}
其实单单看函数名,已经知道这是构造默认的 gin engine ,可以看到 engine 是通过 New() 方法得到的,我们选忽略第一行和第三行。
// gin.go
func New() *Engine {
debugPrintWARNINGNew() // 忽略
engine := &Engine{
RouterGroup: RouterGroup{ // 路由组,后面再分析
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine, // bool,是否为默认处理 engine
UseRawPath: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine // 重点,将 engine 重新赋值给路由组对象中的 engine
engine.pool.New = func() interface{} { // 重点,资源池
return engine.allocateContext()
}
return engine
}
到这里,我们不用看 engine 结构的具体定义,也已经看出来比较多的信息了,主要用路由组和资源池,这两个都可以分别展开写一篇文章,由于篇幅有限,这里就先不介绍了。
值得注意的是这里面有两个地方很巧妙:
engine.RouterGroup.engine = engine
很明显,路由组 RouterGroup 中还有个 engine 的指针对象,为什么要这么设计呢?读者们可以思考一下。
engine.pool.New = func() interface{} { // 对象池
return engine.allocateContext()
}
看下 engine.allocateContext() 方法:
func (engine *Engine) allocateContext() *Context {
return &Context{engine: engine}
}
可以看到 engine 中包含了 pool 对象池,这个对象池是对 gin.Context 的重用,进一步减少开销,关于 sync.pool 对象池我就不在这里细说了, 后续再更新关于 sync.pool 的文章。
engine
最后再来看看 engine 的结构:
type Engine struct {
// 路由组
RouterGroup
// 如果true,当前路由匹配失败但将路径最后的 / 去掉时匹配成功时自动匹配后者
// 比如:请求是 /foo/ 但没有命中,而存在 /foo,
// 对get method请求,客户端会被301重定向到 /foo
// 对于其他method请求,客户端会被307重定向到 /foo
RedirectTrailingSlash bool
// 如果true,在没有处理者被注册来处理当前请求时router将尝试修复当前请求路径
// 逻辑为:
// - 移除前面的 ../ 或者 //
// - 对新的路径进行大小写不敏感的查询
// 如果找到了处理者,请求会被301或307重定向
// 比如: /FOO 和 /..//FOO 会被重定向到 /foo
// RedirectTrailingSlash 参数和这个参数独立
RedirectFixedPath bool
// 如果true,当路由没有被命中时,去检查是否有其他method命中
// 如果命中,响应405 (Method Not Allowed)
// 如果没有命中,请求将由 NotFound handler 来处理
HandleMethodNotAllowed bool
ForwardedByClientIP bool
// #726 #755 If enabled, it will thrust some headers starting with
// 'X-AppEngine...' for better integration with that PaaS.
AppEngine bool
// 如果true, url.RawPath 会被用来查找参数
UseRawPath bool
// 如果true, path value 会被保留
// 如果 UseRawPath是false(默认),UnescapePathValues为true
// url.Path会被保留并使用
UnescapePathValues bool
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
// method call.
MaxMultipartMemory int64
delims render.Delims
secureJsonPrefix string
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
//每个http method对应一棵树
trees methodTrees
}
上面提到过 ServeHTTP 这个方法,其中 engine.handleHTTPRequest(c) 这行代码就是具体的处理操作。
可以看到 engine 的结构中有这么一个字段 RedirectTrailingSlash ,在 Default() 初始化方法中为 true ,我对此比较感兴趣,大家也可以根据注释的意思来测试一下,最终会走到下面的代码中:
func (engine *Engine) handleHTTPRequest(c *Context) {
……
if httpMethod != "CONNECT" && rPath != "/" {
if value.tsr && engine.RedirectTrailingSlash {
// 这里就是尝试纠正请求路径的函数
redirectTrailingSlash(c)
return
}
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
return
}
}
……
}
总结一下
上图就是本文的核心了,可以结合图来理解一下 gin 启动的过程及设计,下一篇会将 gin 的路由,敬请期待~
本系列 “拆轮子系列:gin 框架” 的第一篇就到这里了,这么通读下来,发现 gin 框架的设计和实现真的太棒了,简洁清晰,又不失巧妙,很适合大家也去阅读学习一下,墙裂推荐!!!
欢迎关注我们的微信公众号,每天学习 Go 知识
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 源码分析SpringBoot启动
- kubelet 分析源码:启动流程
- mybatis源码分析:启动过程
- 【zookeeper源码】启动流程详解
- ReactNative源码解析-启动流程
- Android 系统源码-1:Android 系统启动流程源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Data Structures and Algorithm Analysis in Java
Mark A. Weiss / Pearson / 2011-11-18 / GBP 129.99
Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course wa......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!
在线进制转换器
各进制数互转换器
HTML 编码/解码
HTML 编码/解码