当前位置: 首页 > wzjs >正文

广东湛江网站建设培训管理平台

广东湛江网站建设,培训管理平台,秋长网站建设,深圳策划公司排名本文目录 一、Gin框架路由详解Radix Tree 二、源码解析 本文为学习七米老师Gin框架源码解析文章教程的学习笔记。 一、Gin框架路由详解 Gin框架使用的是定制版的httprouter,路由原理是大量使用公共前缀的树结构,它基本上是一个紧凑的Trie tree&#xf…

本文目录

  • 一、Gin框架路由详解
    • Radix Tree
  • 二、源码解析

本文为学习七米老师Gin框架源码解析文章教程的学习笔记。

一、Gin框架路由详解

Gin框架使用的是定制版的httprouter,路由原理是大量使用公共前缀的树结构,它基本上是一个紧凑的Trie tree(或者只是Radix Tree)。具有公共前缀的节点也共享一个公共父节点。

Radix Tree

基数树(Radix Tree)又称为PAT位树(Patricia Trie or crit bit tree),是一种更节省空间的前缀树(Trie Tree)。对于基数树的每个节点,如果该节点是唯一的子树的话,就和父节点合并。下图为一个基数树示例:

在这里插入图片描述
比如访问博客的时候,路径有很多参数和通配符,使用哈希map就不太方便,做一个网站路由不可能无限制的多,也是需要考量的,空间不会特别大。虽然没有哈希map那么快,但是用前缀树也够用了。

比如下面注册路由信息。

r := gin.Default()
r.GET("/", func1)
r.GET("/search/", func2)
r.GET("/support/", func3)
r.GET("/blog/", func4)
r.GET("/blog/:post/", func5)
r.GET("/about-us/", func6)
r.GET("/about-us/team/", func7)
r.GET("/contact/", func8)

那么就会得到一个GET方法对应的路由树,如下所示。
在这里插入图片描述
最右边那一列每个*<数字>表示Handle处理函数的内存地址(一个指针)。从根节点遍历到叶子节点我们就能得到完整的路由表

blog/:post其中:post只是实际文章名称的占位符(参数)。与hash-maps不同,这种树结构还允许我们使用像:post参数这种动态部分,因为我们实际上是根据路由模式进行匹配,而不仅仅是比较哈希值。

路由器为每个请求方法管理一棵单独的树。一方面,它比在每个节点中都保存一个method-> handle map更加节省空间,它还使我们甚至可以在开始在前缀树中查找之前大大减少路由问题。

也就是对于search来说,可能有get、post等请求,相比于单独对search开一个handle map来处理,从请求方法的角度来进行统一划分会更节省了空间,甚至可在查找之前减少路由问题。

为了获得更好的可伸缩性,每个树级别上的子节点都按Priority(优先级)排序,其中优先级(最左列)就是在子节点(子节点、子子节点等等)中注册的句柄的数量。这样做有两个好处:

1、首先优先匹配被大多数路由路径包含的节点。这样可以让尽可能多的路由快速被定位。

2、类似于成本补偿。最长的路径可以被优先匹配,补偿体现在最长的路径需要花费更长的时间来定位,如果最长路径的节点能被优先匹配(即每次拿子节点都命中),那么路由匹配所花的时间不一定比短路径的路由长。下面展示了节点(每个-可以看做一个节点)匹配的路径:从左到右,从上到下。
在这里插入图片描述

二、源码解析

先写一个gin的简单demo。

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "ok")})r.Run()
}

点击Run进去看源码,可以看到调用了http也就是go的net/http网络组件进行开发。

在这里插入图片描述
来看看ListenAndServer这个函数,需要两个参数。

在这里插入图片描述
Handler类型要求必须实现ServerHTTP方法的接口。

在这里插入图片描述
而在gin中,被传进去了engine作为handler,也就是第二个参数,说明其一定实现了ServerHTTP方法。

在这里插入图片描述
这个Engine引擎实现了三个接口,如下所示。

在这里插入图片描述
定位到engine实现 ServeHTTP方法的地方,来看看。

在这里插入图片描述

c := engine.pool.Get().(*Context)

engine.pool是一个对象池(通常是一个sync.Pool实例),用于复用Context对象。Get()方法从对象池中获取一个对象。(Context)是类型断言,确保从池中获取的对象是Context类型。

通过对象池减少每次申请、垃圾回收的资源消耗。

同时取出来之后再做的初始化,也是一个比较重要的点。

首先sync.Pool 是线程安全的,但对池的操作(如 Put 和 Get)仍然会涉及锁机制。如果在归还对象时进行初始化,可能会增加锁的持有时间,从而降低并发性能。

sync.Pool 的设计目标是提供一个通用的对象复用机制,而不是管理对象的状态。也就是对象的状态让使用者来关心,归还对象时只涉及简单的 Put 操作,避免了在归还时进行复杂的初始化逻辑。

接着看handleHTTPRequest函数的源码。

在这里插入图片描述

首先是 请求路径处理与路由树匹配逻辑,在 handleHTTPRequest 函数的前半部分,代码主要负责处理 HTTP 请求的路径信息,并尝试在路由树中找到匹配的路由。首先,根据配置选择使用 Request.URL.RawPath 或 Request.URL.Path 作为路由匹配的基础路径,并决定是否对路径进行解码。如果启用了 RemoveExtraSlash,还会对路径进行清理,去除多余的斜杠。随后,代码遍历 engine.trees,这是一个按 HTTP 方法分类的路由树集合,寻找与当前请求方法匹配的路由树根节点。一旦找到对应的根节点,便调用 root.getValue 方法,尝试在路由树中匹配请求路径,并提取路径参数和对应的处理器。如果找到匹配的处理器,会将相关参数和处理器赋值给 Context,并继续后续处理;如果没有找到匹配的处理器,但满足特定条件(如路径尾部斜杠或路径拼写错误),则会尝试执行重定向操作。

进一步点进来看tengines的结构体,里边会找到一个IRouter。

在这里插入图片描述

var _ IRouter = (*Engine)(nil)

(*Engine)(nil):将 nil 转换为 *Engine 类型。这并不会分配内存,只是创建了一个 *Engine 类型的空值。var _ IRouter = (*Engine)(nil):将 (*Engine)(nil) 赋值给一个匿名变量 _,并声明它是一个 IRouter 类型。

如果 *Engine 没有实现 IRouter 接口,编译器会报错,提示 *Engine 不满足 IRouter 接口的定义。

在这里插入图片描述

在编写完一个结构体之后,通常会写一个匿名的变量,来等于一个空的结构体指针,是为了确保我们的结构体实现了指定的接口。

var _ IRouter = (*Engine)(nil) 是一种常见的类型断言写法,用于确保某个类型实现了某个接口。这种写法的作用是在编译时验证 *Engine 是否满足 IRouter 接口的定义。

如果 Engine 类型实现了 Handle、Group 以及其他所有方法,那么 *Engine 就实现了 IRouter 接口。

在这里插入图片描述
上面这种写法会在很多第三方库中学到。

接着继续看看trees的源码。

在这里插入图片描述
没有用map映射的方式去存请求方法和映射,好处就是节省内存。因为请求方法http1.1中一共就9个。只需要定义一个请求的切片即可。比map的内存会节省。

engine的new方法中,可以看到trees是一次性申请了9的容量。(可以一次性把切片容量申请到位,这样后面不需要动态扩容slice的容量。)

在这里插入图片描述
接着来看看node的详细字段。

在这里插入图片描述

type node struct {path      string         // 当前节点的路径片段,例如 "/user" 或 ":id"indices   string         // 路径片段的索引信息,用于快速匹配,和children字段对应, 保存的是分裂的分支的第一个字符, 例如search和support, 那么s节点的indices对应的"eu"wildChild bool           // 是否有子节点是通配符(如 `:param` 或 `*catchall`),也就是节点是否是参数节点nType     nodeType       // 节点类型(如静态节点、参数节点、通配符节点等)priority  uint32         // 节点的优先级,用于排序和匹配时的优先级判断children  []*node        // 子节点列表,最多包含一个 `:param` 风格的节点在数组末尾handlers  HandlersChain  // 与该节点关联的处理器链fullPath  string         // 完整的路径,从根节点到当前节点的路径字符串
}

接下来我们来看看怎么添加路由的。GET方法是属于RouterGroup的类型的,但是能被Eegine的r调用,说明肯定是在Engine中进行了嵌套调用。

在这里插入图片描述

来看看,确实看到对应的Engine中包含。
在这里插入图片描述

GET方法的两个参数,分别是string还有HandlerFunc类型的切片。

在这里插入图片描述

接着来看,group.handle()做了什么事情。
在这里插入图片描述

combineHandlers就是把我们注册的handlerfunc给拼接了一下。group本身的处理函数切片还有我们传过来的handlers拼接,组成一个处理函数的完整切片(调用链)。

combineHandlers 函数的作用是将 RouterGroup 的现有处理器链(group.Handlers)与传入的处理器链(handlers)合并。它首先计算合并后的处理器链的总长度,确保合并后的处理器数量不会超出限制(通过 assert1 断言)。接着,函数创建一个新的处理器链(mergedHandlers),并使用 copy 函数将 group.Handlers 和 handlers 的内容依次复制到新链中。最终返回合并后的处理器链。

在这里插入图片描述
接着我们回过头来看看addRoute函数的作用。

在这里插入图片描述

在这里插入图片描述
接着往下看可以发现 node 的addRoute函数,也就是构造路由树的方法。

核心的就是insertChild方法。

然后就是先判断是否是空树,如果不是,就继续walk下去。
在这里插入图片描述

http://www.dtcms.com/wzjs/323000.html

相关文章:

  • 郑州seo外包费用宁波seo网络推广
  • 做网站推广排名免费个人网站制作
  • 如何做社团网站网页设计制作网站模板图片
  • 烟台建设科技网站厦门seo结算
  • 专业企业网站建设公司免费建一级域名网站
  • 网站建设销售话术驻马店百度seo
  • 做二维码网站推广网上国网
  • 地方政府网站建设的建议杭州优化外包哪里好
  • 企业网站建设规划ppt问答推广
  • 门户网站建设成都百度客服电话24小时客服电话
  • 用织梦做网站有钱途吗免费网站alexa排名查询
  • 学中文网站湖北网站设计
  • 网站关键词优化代码什么是seo什么是sem
  • 网上购物商城网站头条广告入口
  • 今日闵行公告东莞seo建站
  • 嘉兴网站建设方案托管今日新闻头条10条
  • 周到的商城网站建设seo公司 彼亿营销
  • 建设集团网站公司解析域名网站
  • 学生兼职做网站文件关键词搜索工具
  • 一般做网站是在什么网站找素材网站策划
  • 美篇app制作教程网站优化排名网站
  • 做一名网络写手去那个网站好如何让别人在百度上搜到自己公司
  • 网站设计与规划作业爱站网关键词挖掘工具站长工具
  • 学做标书网站线上营销的方式
  • 网站源码查询百度店铺怎么开通
  • dedecms双语网站东莞搜索排名提升
  • 义乌义亭招工做网站养猪工作代做百度收录排名
  • 网站建设和制作怎么赚钱百度推广助手app下载
  • 苏州公司网站建设方案中国品牌策划公司排名
  • 拼多多代运营魔方优化大师官网下载