Hello World
众所周知, 编程起源Hello World
, 让俺们从Hello World
开始吧.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default() // 返回一个Engine实例, 它包含路由, 中间件和配置信息
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
gin.Engine
gin.Default()
返回了一个Engine
对象, 构造如下
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
type Engine struct {
// Engine组合了RouterGroup, RouterGroup在内部用于配置router, RouterGroup与一个前缀和一个处理程序数组(中间件)相关联。
RouterGroup
RedirectTrailingSlash bool
RedirectFixedPath bool
HandleMethodNotAllowed bool
ForwardedByClientIP bool
AppEngine bool
UseRawPath bool
UnescapePathValues bool
MaxMultipartMemory int64
RemoveExtraSlash bool
delims render.Delims
secureJSONPrefix string
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
maxParams uint16
}
这里比较重要的就是RouterGroup
, 路由注册,分组和嵌套的方法全部来自于他
1
2
3
4
5
6
type RouterGroup struct {
Handlers HandlersChain
basePath string // 用于路由分组的时候
engine *Engine
root bool
}
Default
所做的事情就是调用New
返回一个不带中间件的Engine
并且添加两个中间件. New
则返回一个Engine
对象, 再把自己放到RouterGroup
中
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
...
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
到现在Default
的工作就完成惹
路由注册
路由注册的方法最后都会执行
1
2
3
4
5
6
7
// 所有的路由注册都会执行这个方法
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath) // 获取完整路由
handlers = group.combineHandlers(handlers) // 把RouterGroup的handler和函数的handler结合起来(中间件)
group.engine.addRoute(httpMethod, absolutePath, handlers) // 注册
return group.returnObj()
}
下面主要研究engine.addRoute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
...
root := engine.trees.get(method) // 从路由树切片中获取该方法的树的根节点, 返回一个node
if root == nil { // 如果没有根节点的话, 就新建一个根节点
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers) // 组装路由树, 这个就不多写了
// Update maxParams
if paramsCount := countParams(path); paramsCount > engine.maxParams {
engine.maxParams = paramsCount
}
}
这里还有node
这个类
1
2
3
4
5
6
7
8
9
10
type node struct {
path string
indices string
wildChild bool
nType nodeType
priority uint32
children []*node // 子节点
handlers HandlersChain // 注册的方法
fullPath string
}
开始执行
这里执行使用的是Run
方法, Run
方法调用了http.ListenAndServe
, 并将engine
作为handler
传了进去
1
2
3
4
5
6
7
8
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) // ListenAndServe接受一个Handler
return
}
请求来临时, 就会执行Engine
的ServeHTTP
方法, Engine
的ServerHTTP
方法如下
1
2
3
4
5
6
7
8
9
10
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context) // engine.pool 优化GC
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c) // 处理Context
engine.pool.Put(c)
}
handleHTTPRequest
方法
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
func (engine *Engine) handleHTTPRequest(c *Context) {
...
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
// Find route in tree
value := root.getValue(rPath, c.params, unescape)
if value.params != nil {
c.Params = *value.params
}
// 开始执行handlers
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
...
break
}
...
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
上面的Next是不是很熟悉, 我们在写中间件的时候经常会用到这个方法
1
2
3
4
5
6
7
func (c *Context) Next() {
c.index++ // c.index记录当前执行到哪个中间件
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
reset
会把Context.index
设置成-1, 所以每次进入循环会取handlers中的第0个开始执行, 遇到Next就回去执行下一个. 这样就形成了一个函数栈.
http.ListenAndServe
这个是属于http
包中的内容, 这里也说一下好了, ListenAndServe
接受一个地址和一个handler, 之后便启动一个server
1
2
3
4
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
接下来便是这两个方法
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
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
func (srv *Server) Serve(l net.Listener) error {
...
for {
// 这里不断的监听请求
rw, e := l.Accept()
...
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
// 并且启用协程去处理
go c.serve(ctx)
}
}
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}
结构体conn
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
type conn struct {
server *Server
rwc net.Conn
}
func (c *conn) serve(ctx context.Context) {
...
for {
...
// 然后看着一句就好了, c.server就是Server对象
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler // handler就是Server对象的Handler, 也就是ListenAndServe时传进来的Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req) // 最后执行的就是传进来的Handler的ServeHTTP方法
}
总结
强类型的源代码还是比较容易阅读的, 不会突然的多出来一些方法和属性. 这里把gin的基础结构说了一下, 至于路由树的构造没有详细的去看, 大概就是每个方法对应着一棵树, 最短公共路由下包含着子路由. goto语句不是太好理解.