【Gin框架】理解Gin的中间件实现原理

中间件的实现原理

中间件的实现有:链式调用, 拦截器,注册中心 等方式,Gin框架使用的是 链式调用 + 洋葱模型 。

链式调用

链式调用,顾名思义就是像一个链子一样连在一起,一个一个的别调用。

Gin框架中的是一个切片:

go 复制代码
// HandlerFunc将gin中间件使用的处理程序定义为返回值。
type HandlerFunc func(*Context)

//HandlersChain定义HandlerFunc切片。
type HandlersChain []HandlerFunc

一个路由需要经历的所有中间件函数以及最终的处理函数都会被按照添加的顺序添加到函数链中并储存在路由树的节点上。

怎么调用中间件

在Gin中实现了http包的Handler接口的ServerHTTP方法,(调用流程在Gin启动和接收请求的文章中介绍)。经过ServerHTTP方法,初始化了Context结构体,给其中index这个字段设为-1,调用c.Next时会调用到中间件函数。

ServerHTTP

go 复制代码
// ServeHTTP实现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()//初始化Context

	engine.handleHTTPRequest(c)//在这里边调用了c.Next(),

	engine.pool.Put(c)
}

c.Next

go 复制代码
// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)//这个就是函数链,调用第index的函数,参数为c。
		c.index++
	}
}

c.Abort

在中间件中还有一个Abort函数,作用是终止执行。

go 复制代码
// Abort阻止调用挂起的处理程序。请注意,这不会停止当前处理程序。
//假设你有一个授权中间件,它验证当前请求是否被授权。
//如果授权失败(例如:密码不匹配),
//则调用Abort以确保不调用此请求的其余处理程序。

const abortIndex int8 = math.MaxInt8 >> 1

func (c *Context) Abort() {
	c.index = abortIndex
}

洋葱模型

就像这个洋葱一样,链式函数是一层一层的调用的。

我们看一下Gin框架官方的两个中间件,他们很好的体现了洋葱模型。

go 复制代码
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

Logger

go 复制代码
func Logger() HandlerFunc {
	return LoggerWithConfig(LoggerConfig{})
}

func LoggerWithConfig(conf LoggerConfig) HandlerFunc {

	//省略初始化日志。

	return func(c *Context) {
	...

		c.Next()

		//省略将日志格式化输出
		}
	}
}

通过前面我们知道,调用c.Next函数的时候会调用下一个函数链中下一个函数,这时候LoggerWithConfig函数还没有执行完,就开始执行下一个函数了,当这个c.Next执行完成后,才会去执行下面的语句了。这个过程是不是就像是一层包一层,类似于洋葱一样。

Recovery

go 复制代码
func Recovery() HandlerFunc {
	return RecoveryWithWriter(DefaultErrorWriter)
}

func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc {
	if len(recovery) > 0 {
		return CustomRecoveryWithWriter(out, recovery[0])
	}
	return CustomRecoveryWithWriter(out, defaultHandleRecovery)
}

func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
	...
	return func(c *Context) {
		defer func() {
			//基本功能是去捕获异常的
		}()
		c.Next()
	}
}

Recovery的做作用是捕获异常的,defer后的函数的作用是去捕获c.Next函数后的异常,防止程序崩溃。具体原理可以去看看defer,painc和recovery的运行机制。

相关推荐
北冥you鱼3 天前
Gin 架构在区块链应用中的最佳实践
架构·区块链·gin
赫媒派6 天前
Gin 12年零破坏API,架构哲学如何练成?
后端·go·gin
呆萌很1 个月前
【Gin】文件上传下载(单文件 + 多文件)
gin
先跑起来再说1 个月前
Go 排行榜系统的工程化实现:分布式锁、快照表与定时刷新
分布式·go·gin
必胜刻1 个月前
一个异步生成游戏功能的落地复盘:Redis Stream + WebSocket + 状态补偿
redis·websocket·golang·gin·状态补偿
TE-茶叶蛋1 个月前
B-tree vs GIN Trigram vs HNSW
gin
曾几何时`1 个月前
Go(一)Gin框架 和 GORM机制
开发语言·golang·gin
老毛肚1 个月前
Gin + GORM 接口可视化测试
gin
lolo大魔王1 个月前
Gin 框架中间件超详细实战教程(原理、全局中间件、路由中间件、自定义中间件、跨域、日志拦截)
中间件·gin
lolo大魔王1 个月前
Gin 框架响应格式与 HTML 模板渲染完整实战教程
前端·html·gin