• go web framework gin middleware 设计原理


    场景:一个middleware可以具体为一个函数,而由前面的gin 路由分析可得,每一个路径都对有一个HandlersChain 与其对应。

    那么实际上增加一个middleware的过程,就是将每一个路由策略加进来之前,与其绑定,这样就能使得这一类的路由到来的时候触发这个中间件生效。

    下面看看gin web framework中是如何实现的?

    首先是:gin.default()函数

    // Default returns an Engine instance with the Logger and Recovery middleware already attached.
    func Default() *Engine {
    	debugPrintWARNINGDefault()
    	engine := New()
    	engine.Use(Logger(), Recovery()) #加载Logger(), 和Recovery()中间件
    	return engine
    }

    然后查看engine.Use()

    // Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
    // included in the handlers chain for every single request. Even 404, 405, static files...
    // For example, this is the right place for a logger or error management middleware.
    func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    	engine.RouterGroup.Use(middleware...) #这个函数的参数是一个不定参数,也就是说我们可以追加的中间件没有限制
    	engine.rebuild404Handlers()
    	engine.rebuild405Handlers()
    	return engine
    }
    

    接着是engine.RouterGroup.Use()

    // Use adds middleware to the group, see example code in GitHub.
    func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    	group.Handlers = append(group.Handlers, middleware...) #将middleware暂存在group.Handlers中
    	return group.returnObj()
    }

    这个时候,我们再来看一个路由信息的调用过程,如,我们定义一个Get的路由

    // Ping test
    r.GET("/ping", func(c *gin.Context) {
    	c.String(http.StatusOK, "pong")
    })
    

    查看r.GET函数的实现

    // GET is a shortcut for router.Handle("GET", path, handle).
    func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    	return group.handle("GET", relativePath, handlers) #将handlers 函数以及相对路径传递给group.handle()函数
    }
    

     查看group.handle()函数的实现

    func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    	absolutePath := group.calculateAbsolutePath(relativePath) #计算绝对路径
    	handlers = group.combineHandlers(handlers) #将参数的handlers 和 middleware 的handlers拼接在一起作为一条路由信息,传递给addRoute()函数
    	group.engine.addRoute(httpMethod, absolutePath, handlers)
    	return group.returnObj()
    }
    

     查看group.combineHandlers()函数的实现

    func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
    	finalSize := len(group.Handlers) + len(handlers)
    	if finalSize >= int(abortIndex) {
    		panic("too many handlers")
    	}
    	mergedHandlers := make(HandlersChain, finalSize) #创建一个HandlersChain的数组,然后把group.handlers 和 参数的handlers copy 进去
    	copy(mergedHandlers, group.Handlers)
    	copy(mergedHandlers[len(group.Handlers):], handlers)
    	return mergedHandlers
    }
    

     于是每一条路由的达到,如果根据基树找到这里的HandlersChain,那么就会触发该HandlersChain中所有的func, 至于如何找到的,查看gin 启动流程分析这篇文章。

    这样的中间件设计有什么优缺点呢?

    优点:保证所有的路由信息都会用到中间件。

    缺点:如果中间件并不适用于所有的路由策略,则更改起来比较麻烦,需要在中间件内部根据context做判断。

  • 相关阅读:
    POJ 2942 圆桌骑士 (点双学习笔记)
    洛谷P3563 POI Polarization
    通过集群的方式解决基于MQTT协议的RabbitMQ消息收发
    在WebAPI中调用其他WebAPI
    将WebAPI发布到本地服务器与远程服务器
    利用RabbitMQ、MySQL实现超大用户级别的消息在/离线收发
    C#程序调用cmd.exe执行命令
    C#调用python
    Trixbox下SIP TRUNK的基本设定
    连接两台asterisk服务器
  • 原文地址:https://www.cnblogs.com/Spider-spiders/p/10235385.html
Copyright © 2020-2023  润新知