gin的初始化十分简单,我们通过如下请求就可以完成一个简单的gin服务的启动。
func main() {
r := gin.Default() // 加载gin引擎
// 注册路由
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// 启动Gin Server
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
创建Engine对象
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New() // 按照默认条件创建一个engine对象供gin server全局使用
engine.Use(Logger(), Recovery()) // 注册Logger和Recovery这两个中间件
return engine //返回准备好的引擎对象
}
其中New中的实现十分的简答,就是 &Engine对象,没什么可以细究的,在New过程中我们需要关注的最主要的就是RouterGroup和trees,一个是存储的请求路径,一个是映射方法。
RouterGroup:主要存储了路由的基本信息,以及路径映射,我们实现的所有的路由都在此处。
trees:是一个methodTrees类型的slice,用于存储HTTP方法树的根节点,每个节点代表一个路由。
RouterGroup
gin中实现了所有的基础请求的接口,我们可以在routergroup.go文件中看到。
// POST is a shortcut for router.Handle("POST", path, handlers).
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodPost, relativePath, handlers)
}
// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
这里,我们启动阶段创建的RouterGroup就出现了,这个就是装载所有路由的地方,我们所有的路由请求都在这里完成注册。
路径注册过程页十分简单。
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加入到调用链中去。
- 利用addRoute方法实现路由注册,其中addRouter方法是技术就是对我们之前New阶段所提到的Trees进行一个树操作,把调用链注册进去。
- 最后把路由返回回去,就完成了一个API的注册工作。
Run
func (engine *Engine) Run(addr ...string) (err error) {
// 获取http服务监听路径
address := resolveAddress(addr)
// 开启http服务监听
err = http.ListenAndServe(address, engine.Handler())
return
}
Run的思路就非常简单了。
- 获取http服务的路径和端口。
- 开启服务。
其中resolveAddress中就可以看出为什么当我们什么都没有设置的时候,默认端口是8080了
func resolveAddress(addr []string) string {
switch len(addr) {
case 0:
if port := os.Getenv("PORT"); port != "" {
debugPrint("Environment variable PORT="%s"", port)
return ":" + port
}
return ":8080"
case 1:
return addr[0]
default:
panic("too many parameters")
}go
}
engine.Handler()主要是获取一个http handler对象。
func (engine *Engine) Handler() http.Handler {
if !engine.UseH2C {
return engine
}
h2s := &http2.Server{}
return h2c.NewHandler(engine, h2s)
}
上述代码主要用意是判断是否是http2的服务,如果是就需要用http2的handler,如果不是就正常返回。
最后Gin Server就成功运行起来啦。
这里肯定有小伙伴要疑惑,为什么返回engine就可以了呢?
答案就是,http.handler是一个接口对象,而engine中实现了http.handler中的对象,所以我们可以将engine视为http.handler的实现类。
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
说Go也许不直观,我们用Java的继承实现的思路打开,就可以瞬间理解了。
public interface HttpHandler {
public void ServeHTTP(ResponseWriter, *Request);
}
public class Engine implements HttpHandler {
public void ServeHTTP(ResponseWriter, *Request) {
// do handlers
}
}
这样是不是就好理解了,今天的笔记就到这里,明天我们继续研究gin源码。