Gin 源码初探

HTTP处理

Gin是基于的GOnet/http库编写的,这个库本身就是天然并发的。运行函数Engine.run()便是调用net/http库的ListenAndServe函数:

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
}

ListenAndServe会对每个请求调用Handler接口中的ServeHTTP方法:

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

func ListenAndServe(addr string, handler Handler) error

Handler接口被Gin实现了,实现内容不复杂:首先构建出Context上下文,然后调用handleHTTPRequest方法找到和Request路径匹配的handler并执行(handleHTTPRequest方法还会执行中间件的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)
}

Context

上文中handleHTTPRequest传入Context上下文,它的作用是什么?
答:它其实就是一些函数封装,和保存着请求路径、参数、类型等HTTP上下文信息。它会作为参数传入用户的请求处理函数中,用户使用它可以拥有很多便捷的功能,比如它封装了返回时的Content-Type,若想返回Json body只需调用c.JSON(status, bodyContent),避免了写一大堆代码。

type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
    // 中间件执行函数 + 用户执行函数
	handlers HandlersChain
    // handlers 的下标,以确定当前正在调用哪个执行函数
	index    int8

	engine *Engine
    ...
}

中间件

中间件是什么?Gin怎么加载中间件的?

中间件是什么

  • 非业务的技术组件都叫中间件,mongo、mysql、redis、mongoDriver、GoORM、Mybatis、Hibernate都可以称之为中间件。
  • Gin 的中间件相对狭义,通过源码发现,它的中间件只是在请求前后进行自定义处理。Gin的中间件也有很多,常见的:Logrus、JWT等。

Gin加载中间件

通过调用EngineUse方法将中间件处理函数添加到路径分组中,即是添加到RouterGroupHandlers切片中:

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	...
	return engine
}

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

中间件的执行

到了这里,中间件已被添加到RouterGrouphandlers了,它什么时候被调用?
其实上文提到handleHTTPRequest方法便是调用中间件的地方,先上代码再讨论:

func (engine *Engine) handleHTTPRequest(c *Context) {
    // something
    ...
    
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
        // handlers切片 = 各中间件的执行函数 + 该请求处理函数。
        handlers, params, tsr := root.getValue(rPath, c.Params, unescape)
		if handlers != nil {
			c.handlers = handlers
			c.Params = params
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
        
        // something
        ...
    }
    // something
    ...
}

func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

通过Next()可以看到,各个中间件的处理函数会被依次执行,而请求处理函数是最后加入handlers的,也就最后执行。这样也就实现了:在请求处理函数执行前,执行中间件自定义处理。
若想在请求处理函数执行后,执行中间件的自定义处理,怎么办?中间件自己调用ContextNext()函数即可,就能实现:中间件中Next()之前的逻辑在请求处理函数之前执行;Next()之后的逻辑在请求处理函数之后执行。

(刚从Java转Go,共同学习,一起进步,觉得有些许收获可以点波赞,若有不对之处谢谢留言指出)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值