Go Gin web框架的路由原理及中间件原理

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
性能测试 PTS,5000VUM额度
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: Go Gin web框架的路由原理及中间件原理

一、Gin框架的路由原理:

参考:

首先了解下什么是路由?

简而言之,http路由即是一条http请求的“向导”,根据URI上的路径,指引该条请求到对应的方法里去执行然后返回,中间可能会执行一些中间件。其次,路由又分为 静态路由,动态路由...

  • 静态路由: 框架/用户提前生成一个路由表,一般是map结构,key为URL上的path,value为代码执行点(处理函数),

    • 优点:只需要读取map,没有任何开销,速度奇快
    • 缺点:无法正则匹配路由,只能逐一对应,模糊匹配的场景无法使用
  • 动态路由: 用户定义好路由匹配规则,框架匹配路由时,根据规则动态的去规划路由

    • 优点:适应性强,解决了静态路由的缺点
    • 缺点:相比静态路由有开销,具体视算法和路由匹配规则而定

gin框架作为一个轻量级的web框架,采用的是字典树(前缀树)的方式实现的动态路由。当 gin 注册路由时,会根据不同的 Method 分别注册不同的路由树,比如 这四个请求,会分别注册四棵路由树出来:

GET    /user/{userID} HTTP/1.1
POST   /user/{userID} HTTP/1.1
PUT    /user/{userID} HTTP/1.1
DELETE /user/{userID} HTTP/1.1

在这里插入图片描述

gin路由注册的示例代码及其前缀树示意图,分别如下:

r := gin.New()
r.GET("/user/:name", routeUser)

func routeUser(c *gin.Context){
    //todo something
}

在这里插入图片描述

关于更多字典树的细节可以看这里

  • 字典树(Trie树):是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计

    • 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
    • 复杂度分析:字典树其实是一种用空间换时间的算法,它占用的空间一般很大,但非常高效,插入和查询的时间复杂度都是 O(1) 的。

然而普通的字典树每个节点只能存储一个字符,这意味着面对较长的字符串仍然要向下探寻多个节点,这存在着浪费,因此就有了压缩字典树。

  • 压缩字典树:是trie树的一种,也称单词查找树(前缀树),善于进行字符串检索、取字符串最长公共前缀、以及排序,常应用在搜索引擎中,例如:百度输入某个字可能自动弹出匹配到的词组出来

在这里插入图片描述

  • ③ 字典树与压缩字典树的区别

    • 压缩字典树 每个节点不仅仅只存一个字符,而是取字符串的最长公共前缀。
    • 压缩字典树和标准字典树最大的不同点就节点的数量与插入字符串的个数成正比,而不是与字符串的长度成正比,所以当字符串数量越来越多,越密集且相似度极高的情况下,会退化成标准trie树。

下面分别是 /,/bear,/bell,/bid,/bull,/buy,/sell,/stock,/stop标准tire压缩 tire 的示意图:
在这里插入图片描述
在这里插入图片描述

  • ④ 关于字典树的查询操作 过程如下图:

    1. 先找共同前缀,比如下图中的s

      1. 再找目录 indices,比如下图中的eu
      2. 循环上面两步,直至当前path满足条件

在这里插入图片描述

二、Gin框架的中间件原理(Middleware)

参考:

中间件是为了过滤路由而发明的一种机制,也就是http请求来到时先经过中间件,再到具体的处理函数。

首先,Gin框架的中间件是基于洋葱模型的,如下图:
在这里插入图片描述
beforeFunc1和afterFunc1即是中间件1;afterFunc2和afterFunc2即是中间件2。

过程:请求到来时从最外层开始执行中间件1,然后进入第二层,依次执行完所有中间件最后到达主体函数,接着再一层一层的往外走再次执行中间件2...中间件1...最后返回,也有点像栈的概念。

其次,再来看下gin.Context的结构体的主要字段:

// gin.Context 结构体
type Context struct {
    ...
    handlers HandlersChain         // 函数指针切片对象
    index      int8                // 对应函数指针切片中的索引下标,执行c.Next()时会向后移动index下标位置
    ...
}

type HandlerFunc func(*Context)  // 函数指针
type HandlersChain []HandlerFunc // 函数指针切片

可以看到,gin框架的中间件函数和处理函数是以切片形式的调用链条存在的(本质上就是函数指针切片
在我们初始化了gin对象之后:r := gin.New()

  • 注册 [中间件函数/路由处理函数] 的过程:

r.Use(),也就是不断的在上述的 HandlersChain 函数指针切片后执行append操作,去依次向调用链条追加新注册的中间件函数

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}
  • 调用 [中间件函数/路由处理函数] 的过程:

如下图,gin框架正是通过移动切片下标 index 的位置,实现中间件的不断向后调用...
但是这个index要如何去移动呢?这就是gin框架中 c.Next()的作用了,这个函数会在每次调用时将index向后移动,从而依次调用已注册的中间件。

Gin框架的中间件调用过程原理图

func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++
    }
}
  • 停止调用后续 [中间件函数/路由处理函数] 的过程:

c.Abort()方法会阻止调用后续的中间件处理函数,正是因为它使得index移动到切片末尾了,所以后面的 [中间件/路由处理函数] 都没法继续执行了。

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

因此,gin框架的 [中间件函数/路由处理函数] 实际上都是以切片的形式的调用链条存在的(本质上就是函数指针切片),我们可以顺序调用也可以借助 c.Next() 方法实现嵌套调用。

三、总结

  1. gin框架路由使用字典树(前缀树),路由注册的过程是构造前缀树的过程,路由匹配的过程就是查找前缀树的过程。
  2. gin框架的 [中间件函数和处理函数] 是以切片的形式的调用链条存在的(本质上就是函数指针切片),我们可以顺序调用也可以借助 c.Next() 方法实现嵌套调用。
  3. 借助c.Set()和c.Get()方法我们能够在不同的中间件函数中传递数据。
目录
相关文章
|
24天前
|
Cloud Native JavaScript API
一文读懂云原生 go-zero 微服务框架
一文读懂云原生 go-zero 微服务框架
|
11天前
|
消息中间件 NoSQL Go
PHP转Go系列 | ThinkPHP与Gin框架之Redis延时消息队列技术实践
【9月更文挑战第7天】在从 PHP 的 ThinkPHP 框架迁移到 Go 的 Gin 框架时,涉及 Redis 延时消息队列的技术实践主要包括:理解延时消息队列概念,其能在特定时间处理消息,适用于定时任务等场景;在 ThinkPHP 中使用 Redis 实现延时队列;在 Gin 中结合 Go 的 Redis 客户端库实现类似功能;Go 具有更高性能和简洁性,适合处理大量消息。迁移过程中需考虑业务需求及系统稳定性。
|
1月前
|
Go API 数据库
[go 面试] 分布式事务框架选择与实践
[go 面试] 分布式事务框架选择与实践
|
1月前
|
缓存 Java 编译器
Go 中的内存布局和分配原理
Go 中的内存布局和分配原理
|
24天前
|
SQL JavaScript Go
Go Web 服务框架实现详解
Go Web 服务框架实现详解
|
24天前
|
存储 Prometheus 中间件
2020最佳人气项目之Go Web框架
2020最佳人气项目之Go Web框架
|
30天前
|
缓存 监控 中间件
构建高效的Go语言Web服务器:基于Fiber框架的性能优化实践
在追求极致性能的Web开发领域,Go语言(Golang)凭借其高效的并发处理能力、垃圾回收机制及简洁的语法赢得了广泛的青睐。本文不同于传统的性能优化教程,将深入剖析如何在Go语言环境下,利用Fiber这一高性能Web框架,通过精细化配置、并发策略调整及代码层面的微优化,构建出既快速又稳定的Web服务器。通过实际案例与性能测试数据对比,揭示一系列非直觉但极为有效的优化技巧,助力开发者在快节奏的互联网环境中抢占先机。
|
2月前
|
消息中间件 缓存 IDE
MetaQ/RocketMQ 原理问题之消息队列中间件的问题如何解决
MetaQ/RocketMQ 原理问题之消息队列中间件的问题如何解决
|
1月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
34 0
|
1月前
|
缓存 安全 测试技术
深入理解 go sync.Map - 基本原理
深入理解 go sync.Map - 基本原理
17 0