Go第三方框架--gin框架(一)
序言
Gin框架作为go语言使用最多的web框架,以其快速的响应速度和对复杂http路由配置的支持受到程序员和媛们的喜爱,几乎统治了web市场。但作为一名合格的程序员,要知其然更要知其所以然,不然八股文背的也没有啥意思。本着这个原则鄙人打算站在前人的大腿根上从头到尾梳理下Gin的执行流程,主要涉及两大部分:1. 服务器的建立(重点是:Gin是怎么处理和存储各种不同的路由路径和请求函数体的);2. 客户端的连接(主要涉及根据路由寻找对应函数体来执行具体业务逻辑)
1. gn框架的诞生
1.1 go 原生web框架
go 原生的 web框架 在 net/http 包里,因不是本文重点,所以只简要介绍。
net/http 主要采用 map的原理来 存储 路径和handler 其中 key 是 路径 value 是 handler ,如下图的代码
# ServeMux是一个HTTP请求多路复用器。其中 m 保存了 其请求路径和handler的映射关系。
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryes []muxEntry // slice of entries sorted from longest to shortest.hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {h Handlerpattern string
}
func TestHttp(t *testing.T) {// 创建路由器mux := http.NewServeMux()// 设置路由规则mux.HandleFunc("/hello", hello)mux.HandleFunc("/hello/hell", hello2)mux.HandleFunc("/hel/ww", hello2)mux.HandleFunc("/helw/*ww", hello2)// 创建服务器server := &http.Server{Addr: Addr,WriteTimeout: time.Second * 3, //超时时间Handler: mux, //路由规则 }// 监听端口并提供服务log.Println("Starting httpserver at " + Addr)err := server.ListenAndServe()if err != nil {panic(err)return}log.Fatal()
}func hello(w http.ResponseWriter, r *http.Request) {time.Sleep(1 * time.Second)w.Write([]byte("bye bye ,this is httpServer"))
}func hello2(w http.ResponseWriter, r *http.Request) {time.Sleep(1 * time.Second)w.Write([]byte("bye bye ,this is httpServer"))
}
其 建立的 map如下:

可以看出其确实建立了一个 map来存储 路径和handler的映射关系。采用map形式 查找的速度也比较的块。但是为啥还要采用Gin框架呢。
我们来简要梳理下我们程序员在工作过程中需要啥样的web框架吧
- 需要一个可以处理通配符的框架,比如这种: aa/dd* 虽然我(net/http)不支持但是我 速度快啊
- 需要可以处理中间件的框架 比如 对日志的处理等 虽然我(net/http)不支持但是我 速度快啊
- 需要 支持分组的框架 比如 v1 v2这种不同的版本 虽然我(net/http)不支持但是我 速度快啊
- …
目前看来 net/http不适合这种复杂场景的业务逻辑 当然 Google go开发组 目的只是提供一个简小的web框架,设计目标是简单和通用。go开发组 当然也想到了 要利用开源的优势为各路大神提供大显神通的机会。问题是怎么接入呢,现实世界和虚拟世界的连接入口是 二维码。那gin框架如何接入 net/http 呢,也就是如何在重新利用它的其他功能的情况下,再进行扩展呢 你当然能想到了 这就是 interface 接口。
net/http框架中 确实是 通过实现接口来 进行 路由查找 并找到要执行的 hanlder(例如 上述代码中的hello),这样路由建立模块和路由寻址查找handler模块就可以通过不同实现来形成不同的第三方框架。建立路由模块是第三方包独自完成,而路由寻址查找模块主要是实现了 如下接口:
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
这样第三方框架 就可以复用net/http 的大部分功能(包括最重要的 epoll多路复用),并通过实现 ServeHTTP 接口 来实现自己的 路由查找模块(ps: 路由建立模块需自己建立 这块功能用不到接口) ,说完了 理论 那我们再来梳理下 net/http 从 建立连接 到 ServeHTTP的调用链,来验证下上述是否是这样的。
1.2 原生 net/http的 调用链
以 TestHttp 这个函数为例,调用入口是 server.ListenAndServe() ,其调用链为

可以看到 其最终调用到了 ServeHTTP() 这个函数,而所有第三方框架都是实现了 这个函数。这个函数包括 根据req函数中的 url获取 handler(第三方框架提供) 、处理handler和对客户端返回结果三大块的功能。
这样第三方框架就可以 实现ServeHTTP这个函数来 实现对 handler的获取(怎么建立路由结构是第三方框架自己定义)。
1.3 gin等第三方web框架和net/http关系
让我们脱离源码 来梳理下

可以看到 第三方 框架 主要是头尾两个地方跟 net/http不同 中间还是需要复用net/http代码。后续 讲解会围绕这张流程图展开,其中比较重要的是 圆圈 1、2和3,1 包括 gin 的引擎 engine 其包括建立压缩前缀树的功能并实现了 serverHttp接口 形成了 3,2主要涉及了多路复用技术。
以下的 步骤 2、3、4主要涉及圆圈1 的内容,步骤5 涉及圆圈2 的内容 ,步骤6 涉及圆圈3的内容
2. gin框架简介
2.1 gin框架发展历程
gin 框架早期版本是基于julienschmidt/httprouter 发展而来,julienschmidt/httprouter是一个高性能的http请求器。但是随着gin框架的发展 它逐渐发展出了自己的 路由实现器,实现源码也部分参考 julienschmidt/httprouter 这也就是为什么好多资料都说 gin基于julienschmidt/httprouter 但是你去看它最新的源码却没发现针对 julienschmidt/httprouter的引用。
gin框架之所以运行效率高是因为采用了一种叫 Radix Tree (压缩前缀树)的结构体来存储路由路径,其是一种例如有如下路由:
/aa/bb
/aa/bd
/aa/cc
/ac/dd
/ee/ff
建立压缩前缀树 ,请思考下其建立的树是左边还是右边呢

gin 框架的路由树的建立 就是一步一步建立如上图所示的 压缩前缀树 ps:真是的树的节点比较复杂 但是大体步骤就是如此
那么压缩前缀树 有啥优点呢 gin 为什么使用这种结构来存储器节点呢 直觉上看 我们可以想到两点 1: 这种树形结构 查找的时间复杂度是时间复杂度为 o(k) ,k是字符串的长度 2: 压缩证明其使用的空间比较少 可以看到 路径中 有 6个a 但是 我们树节点中只有 2个。这只是我们直观看出来的,对不对呢,是否还有其他优点呢?
答案: 对,当然有其他优点,这种树 也可以用来 进行通配符的匹配 例如这种 /aa/bb/* ;还可以快速建立路由分组等。这两种优势不是本文的重点,感兴趣的同学可以自行查阅资料。
既然你说gin 路由使用的是 压缩前缀树,口说无凭 我们来验证下吧 顺便看下建立的是左边还是右边的压缩前缀树
2.2 gin框架的使用
下面示例代码为(本文后续围绕下面例子展开代码讲解):
func TestGin(t *testing.T) {// 创建一个默认的路由引擎r := gin.Default()// 当客户端以GET方法请求路径时,会执行后面的匿名函数r.GET("/aa/bb", func(c *gin.Context) { c.JSON(200, gin.H{"route path ": "/aa/bb",}) })r.GET("/aa/bd", func(c *gin.Context) { c.JSON(200, gin.H{"route path ": "/aa/bd",}) })r.GET("/aa/cc", func(c *gin.Context) { c.JSON(200, gin.H{"route path ": "/aa/cc",}) })r.GET("/ac/dd", func(c *gin.Context) { c.JSON(200, gin.H{"route path ": "/ac/dd",}) })r.GET("/ee/ff", func(c *gin.Context) { c.JSON(200, gin.H{"route path ": "/ee/ff",}) })// 以上操作 **主要 涉及 圆圈 1**// 启动HTTP服务,默认在0.0.0.0:8080启动服务 **涉及 net/http 框架处理主逻辑 内部 主要 调用 net/http 包**r.Run()}
对上述代码 debugger 可以得到 r 这个参数的 实例 实力分析如下 其中 压缩前缀树的节点node结构体的结构如下:
type node struct {path stringindices stringwildChild boolnType nodeTypepriority uint32children []*node // child nodes, at most 1 :param style node at the end of the arrayhandlers HandlersChainfullPath string
}
现在只需关注node节点的 path 和 children 这两个参数 node结构体详情会在后续步骤介绍
2.2.1 父节点

可以看到 父节点 path == “/” 且有两个孩子节点
2.2.2 第二层节点

可以看到 第二层 节点 左边节点 path==“a” 孩子个数为2 ;右边节点 pah 是 “ee/ff” 这里就是将节点进行了压缩 ee 和 ff 不用再拆分了。因为 ff是ee的唯一的一个孩子节点 因为寻址路径唯一 所以可以向上合并,以便节省空间。
2.2.3 第三层节点

可以看到 左边节点 path==“a/” 孩子节点个数为2 ;右边节点 path==“c/dd”(压缩了) 孩子节点为空
2.2.4 第四层节点

可以看到 左边 节点 path=“b” ,其有两个孩子节点;右边节点 path=“cc” 其没有孩子节点
2.2.5 第五层 节点

可以看到 左边节点 path==“b” 无孩子节点 ;右边节点 path==“d” 无孩子节点
到这里我们可以看出来其确实是建立了一颗 压缩前缀树。总结下来就是: 1. 孩子节点必须大于1(否则应向上合并)2: 压缩有两层含义 第一层将 路由里面 重复的路径 进行压缩 例如 字母a 压缩后就剩2个;第二层 一个节点有一个子节点时 向上兼并 压缩空间
所以建立的前缀树是 右边的。
r.run()执行后 在浏览器输入 路径 就可以看到 对应的函数被执行(注意:默认端口是8080)结果 如图 这边主要涉及 圆圈 2–>3

2.3 gin框架的执行过程
梳理完毕 压缩前缀树的建立 那现在开始我们梳理下 整个 gin框架的流程图 其实主要是围绕 构建的压缩前缀树展开的 ,我个人比较愿意先学习框架使用,然后再进入细节,这样有一个提纲挈领的抓手,我们就知道这些细节在整体脉络中的位置,不至于陷进去失去了方向感。
通过1.3的图可以看到 gin 框架 大概 分为 三大部分
- 创建 压缩前缀树 并且 将 路由 对应的节点 按照规则 插入树节点 ---- 步骤一(圆圈1)
- 运行 引擎 建立 对tcp套接字的监听 这里采用多路复用技术 进行阻塞 等待链接到来 ---- 步骤二
- 浏览器 输入 url 进行客户端请求 这时 唤醒阻塞的程序 从圆圈2 按照箭头执行顺序 一直执行到 圆圈3,然后在圆圈3 中遍历 压缩前缀树 找到对应的 handler (对于路径 :/aa/bd 其 handler 为:func(c *gin.Context) { c.JSON(200, gin.H{"route path ": “/aa/bd”}) }) 执行后 返回结果 ---- 步骤三
3. gn框架源码–四种重要的结构体
框架一般都会采用面向对象的方式来构建,而面向对象中最重要的核心就是结构体。gin框架四种重要的结构体 分别是 Engine/RouterGroup/Node/context ,其中Engine 包含了 RouteGroup和Node 是gin框架的引擎结构体 ;Node是压缩前缀树的树节点,用来保存 压缩路径和handler,在2.2.1----2.2.5中已经做过简要介绍 ;Context 结构体官方介绍是 gin最重要的结构体 ,它允许我们在中间件之间传递变量,管理流,处理 request 请求体和 respose 响应体。可以说 gin 框架基本上是围绕着这四个结构体来操作的。
3.1 Engine 结构体
type Engine struct {RouterGroup // 路由组 ...... // 这里为了使得文章简短 一些本文讲解没用到的 属性 没有列举 感兴趣的可以自己研究下 // UseH2C enable h2c support.UseH2C bool// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.ContextWithFallback booldelims render.DelimssecureJSONPrefix stringHTMLRender render.HTMLRenderFuncMap template.FuncMapallNoRoute HandlersChainallNoMethod HandlersChainnoRoute HandlersChainnoMethod HandlersChainpool sync.Pool // 这个池化技术 用来存储 Context结构体。 它是 gin框架很重要的结构体 主要用来 处理 request和 respose 请求trees methodTrees // 方法树 针对 Get/Post/Delete 等不同请求 都生成一个树 9种请求 9种树 但实现原理都是相同的 本文只介绍 Get方法,其 包含了 Node 结构体maxParams uint16maxSections uint16trustedProxies []stringtrustedCIDRs []*net.IPNet
}
Engine 结构体 是 gin 框架的入口,其包含了许多属性 但对于 我们学习jin框架核心执行逻辑来说,只需要知道 RouterGroup/pool/trees 这三个就行了
3.2 RouteGroup结构体
type RouterGroup struct {Handlers HandlersChain // 需要处理的 handler 链,一般是 默认hanlder(例如 处理 logger和panic的handlewr)+ group组的中间件handler(例如 鉴权等)+ 用户 注册的 handlerbasePath string // 组的基本路径engine *Engine // gin 框架 引擎root bool // 跟节点
}
RouterGroup 主要是用来 对路由进行操作的,包括对post/get等方法的处理。
3.3 Node 结构体
type node struct {path string // 节点路劲indices string // 其子节点的 path的第一个单词 组成的 字符串 用来快速定位路径寻址时 是否走此孩子节点wildChild boolnType nodeType // 节点类型priority uint32 // 优先级 从左往右 越左侧 优先级越大 优先从左边开始 说明 左边的 重复的路径前缀比较多 同层其优先级越高 子节点越多 ,一般情况下 priority等于其直属孩子节点个数,且如果其直属孩子节点为一个 或者 为空,其 优先级 为 1children []*node // child nodes, at most 1 :param style node at the end of the arrayhandlers HandlersChain // 处理器 存储 节点 handler 链 fullPath string // 全路径 fullPath
}
node 是压缩前缀树子节点,是gin框架之所以速度快的核心原因,也是我们本篇文章重点需要介绍和理解的结构体。
对于节点属性的理解 可以对照着 标题 2.21----2.2.5来理解
3.4 context 结构体
type Context struct {writermem responseWriterRequest *http.RequestWriter ResponseWriterParams Paramshandlers HandlersChainindex int8fullPath stringengine *Engineparams *ParamsskippedNodes *[]skippedNode// This mutex protects Keys map.mu sync.RWMutex// Keys is a key/value pair exclusively for the context of each request.Keys map[string]any// Errors is a list of errors attached to all the handlers/middlewares who used this context.Errors errorMsgs// Accepted defines a list of manually accepted formats for content negotiation.Accepted []string// queryCache caches the query result from c.Request.URL.Query().queryCache url.Values// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,// or PUT body parameters.formCache url.Values// SameSite allows a server to define a cookie attribute making it impossible for// the browser to send this cookie along with cross-site requests.sameSite http.SameSite
}
context 是gin框架最重要的结构体 其包含了对 请求 和 响应的 处理逻辑,可以在中间件之间传递数据流。因不是本文的理解gin框架的需要用到的结构体,暂不做过多介绍,请自行用谷歌百度一下。
相关文章:
Go第三方框架--gin框架(一)
序言 Gin框架作为go语言使用最多的web框架,以其快速的响应速度和对复杂http路由配置的支持受到程序员和媛们的喜爱,几乎统治了web市场。但作为一名合格的程序员,要知其然更要知其所以然,不然八股文背的也没有啥意思。本着这个原则…...
网络安全——笔记
XSS:跨站脚本(Cross-site scripting) XSS 全称“跨站脚本”,是注入攻击的一种。其特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文…...
Maven pom.xml配置详解
pom.xml是什么? pom.xml(项目对象模型-Project Object Model的缩写)是Maven项目的核心配置文件,用于管理项目的依赖、插件、构建配置等。 举例说明:以阿里巴巴的fastjson为例: <?xml version"1.…...
2024深圳国际电线电缆及电源产品展览会
2024深圳国际电线电缆及电源产品展览会 2024 Shenzhen International Coupling, Clutch and Brake Exhibition 时间:2024年08月28-30日 地点:深圳国际会展中心(新馆) 预订以上展会详询陆先生 I38(前三位ÿ…...
如何成功将自己开发的APP上架到应用商店
随着移动应用市场的蓬勃发展,开发一款优秀的APP已成为许多企业和个人的首要选择。然而,成功上架并有效推广APP至关重要。本文将逐步介绍完整的上架流程,包括准备所需材料、注册开发者账户、进行APP备案、提交审核以及上架成功后的推广和维护。…...
Jetson AGX ORIN 配置 FGVC-PIM 神经网络(包含 arm64 下面 torch 和 torchvision 配置内容)
Jetson AGX ORIN 配置 FGVC-PIM 神经网络 文章目录 Jetson AGX ORIN 配置 FGVC-PIM 神经网络配置 ORIN 环境创建 FGVC-PIM 虚拟环境安装 PyTorch安装 torchvision安装其他依赖包 配置 ORIN 环境 首先先配置 ORIN 的环境,可以参考这个链接: Jetson AGX …...
mybatisplus和mybatis兼容问题
Invalid bound statement (not found) 错误 原xml配置 <bean id"sqlSessionFactory" class"org.mybatis.spring.SqlSessionFactoryBean"><property name"mapperLocations" value"classpath:/META-INF/mapper/*.xml"/>&l…...
nodejs安装使用React
1、react安装 首先,确保电脑上具备nodejs环境,之后用 winr 呼出控制台,输入 cmd 命令弹出cmd控制台(小黑框)之后在默认路径输入如下代码 npm i -g create-react-app //全局安装react环境无需选择特定文件夹安装成功后…...
防御性编程,可能是导致被裁员的更大的原因,别被误导了
裁员与反裁员是当前IT界一个经典的话题,作为打工者的猿人常常讨论了N多的防御性编程,代码不可读、代码不好改、代码深度嵌套、代码留bug等等。 其实防御性编程只会让决策者加速解耦你与业务系统: 1、增加代码走查的环节(增加成本…...
Unity与鼠标相关的事件(自己记忆用)
1. OnMouseDown:当用户按下鼠标按钮时调用。 - 参数:MouseEvent,可以用来确定哪个鼠标按钮被按下。 2. OnMouseUp:当用户释放鼠标按钮时调用。 - 参数:MouseEvent,可以用来确定哪个鼠标按钮被释放。…...
模型权重下载方法
下载方法1:git lfs下载 1、在hf-mirror.com中搜索模型,如搜索text2vec-large-chinese, 点击模型进入页面:https://hf-mirror.com/GanymedeNil/text2vec-large-chinese/tree/main 2、git lfs install 3、git clone https://hf-mir…...
JS基础之 数据浅拷贝与深拷贝
一、拷贝背景 JS引用数据类型有两类:基本数据类型和引用数据类型; 基本类型:String,Number,Boolean,Null,Undefined,symbol这6种基本数据类型它们是直接按值存放的,所以…...
FFmpeg开发笔记(十四)音频重采样的缓存
FFmpeg在很多地方都运用了缓存机制,比如《FFmpeg开发实战:从零基础到短视频上线》一书的“3.3.2 对视频流重新编码”介绍了编解码的数据缓存,不单是视频编码过程和视频解码过程有缓存,甚至连音频重采样都用到了缓存。 也就是说&a…...
详解Python面向对象编程(一)
类和对象 面向过程——怎么做? (1)把完成某一需求的所有步骤、从头到尾,逐步实现 (2)根据开发需求,将某些功能独立的代码块封装成一个又一个的函数 (3)最后完成的代码&a…...
一文带你完整了解Go语言IO基础库
作者 | 百度小程序团队 导读 introduction 对于刚接触Golang学习的同学,估计比较难掌握的知识点之一就是文件IO处理,光在基础库里会发现 golang除了io包提供文件处理外,os包,http包,embed包都有提供类似的处理…...
Java基于微信小程序的校园请假系统
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&#…...
Expert Prompting-引导LLM成为杰出专家
ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 如果适当设计提示,对齐的大型语言模型(LLM)的回答质量可以显著提高。在本文中,我们提出了ExpertPrompting,以激发LLM作为杰出专家回…...
Element-Plus下拉菜单边框去除教程
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
免费redis可视化工具windows/mac都可以使用,开源免费
官方地址:RedisInsight | The Best Redis GUI github开源地址:GitHub - RedisInsight/RedisDesktopManager Redis Desktop Manager – Redis可视化管理工具、redis图形化管理工具、redis可视化客户端、redis集群管理工具。 官方下载方式 滚动到页面底…...
PHPCMS v9城市分站插件
PHPCMS自带的有多站点功能,但是用过的朋友都知道,自带的多站点功能有很多的不方便之处,例如站点栏目没法公用,每个站点都需要创建模型、每个站点都需要单独添加内容,还有站点必须静态化。如果你内容很多这些功能当然无…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
python基础语法Ⅰ
python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器,来进行一些算术…...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
