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.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}
type muxEntry struct {
	h       Handler
	pattern 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框架吧

  1. 需要一个可以处理通配符的框架,比如这种: aa/dd* 虽然我(net/http)不支持但是我 速度快啊
  2. 需要可以处理中间件的框架 比如 对日志的处理等 虽然我(net/http)不支持但是我 速度快啊
  3. 需要 支持分组的框架 比如 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      string
	indices   string
	wildChild bool
	nType     nodeType
	priority  uint32
	children  []*node // child nodes, at most 1 :param style node at the end of the array
	handlers  HandlersChain
	fullPath  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. 创建 压缩前缀树 并且 将 路由 对应的节点 按照规则 插入树节点 ---- 步骤一(圆圈1)
  2. 运行 引擎 建立 对tcp套接字的监听 这里采用多路复用技术 进行阻塞 等待链接到来 ---- 步骤二
  3. 浏览器 输入 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 bool

	delims           render.Delims
	secureJSONPrefix string
	HTMLRender       render.HTMLRender
	FuncMap          template.FuncMap
	allNoRoute       HandlersChain
	allNoMethod      HandlersChain
	noRoute          HandlersChain
	noMethod         HandlersChain
	pool             sync.Pool   // 这个池化技术 用来存储 Context结构体。 它是 gin框架很重要的结构体 主要用来 处理 request和 respose 请求
	trees            methodTrees   // 方法树 针对 Get/Post/Delete 等不同请求 都生成一个树 9种请求 9种树 但实现原理都是相同的 本文只介绍 Get方法,其 包含了 Node 结构体
	maxParams        uint16
	maxSections      uint16
	trustedProxies   []string
	trustedCIDRs     []*net.IPNet
}

Engine 结构体 是 gin 框架的入口,其包含了许多属性 但对于 我们学习jin框架核心执行逻辑来说,只需要知道 RouterGroup/pool/trees 这三个就行了

3.2 RouteGroup结构体
type RouterGroup struct {
	Handlers HandlersChain   // 需要处理的 handler 链,一般是 默认hanlder(例如 处理 logger和panic的handlewr)+ group组的中间件handler(例如 鉴权等)+ 用户 注册的 handler
	basePath string  // 组的基本路径
	engine   *Engine // gin 框架 引擎
	root     bool // 跟节点
}

RouterGroup 主要是用来 对路由进行操作的,包括对post/get等方法的处理。

3.3 Node 结构体
type node struct {
	path      string  // 节点路劲
	indices   string // 其子节点的 path的第一个单词 组成的 字符串 用来快速定位路径寻址时 是否走此孩子节点
	wildChild bool
	nType     nodeType // 节点类型
	priority  uint32  // 优先级 从左往右 越左侧 优先级越大 优先从左边开始 说明 左边的 重复的路径前缀比较多 同层其优先级越高 子节点越多 ,一般情况下 priority等于其直属孩子节点个数,且如果其直属孩子节点为一个 或者 为空,其 优先级 为 1
	children  []*node // child nodes, at most 1 :param style node at the end of the array
	handlers  HandlersChain  // 处理器 存储 节点 handler 链 
	fullPath  string // 全路径 fullPath 
}

node 是压缩前缀树子节点,是gin框架之所以速度快的核心原因,也是我们本篇文章重点需要介绍和理解的结构体。
对于节点属性的理解 可以对照着 标题 2.21----2.2.5来理解

3.4 context 结构体
type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
	handlers HandlersChain
	index    int8
	fullPath string

	engine       *Engine
	params       *Params
	skippedNodes *[]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框架的需要用到的结构体,暂不做过多介绍,请自行用谷歌百度一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/488666.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java程序设计】【C00372】基于(JavaWeb)Springboot的社区网格化管理系统(有论文)

TOC 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,博客中有上百套程序可供参考,欢迎共同交流学习。 项目简介 项目获取 🍅文末点击卡片…

工业互联网下的增强现实

工业互联网下的增强现实 1、 概述 增强现实(Augmented Reality,简称AR),增强现实技术也被称为扩增现实,AR增强现实技术是促使真实世界信息和虚拟世界信息内容之间综合在一起的较新的技术内容,其将原本在现…

Apache SeaTunnel和SeaTunnel Web 安装部署

Apache SeaTunnel和SeaTunnel Web 安装部署 前面我们介绍已经介绍过了Apache SeaTunnel,这里我们看一下SeaTunnel 的安装部署,早期的SeaTunnel 是没有web 页面的,只能在命令行里使用,现在SeaTunnel 已经有了web 端了,这就降低了我们的使用门槛 下载配置 我们可以去下面的…

如何理解TCP/IP协议?

一、是什么 TCP/IP,传输控制协议/网际协议,是指能够在多个不同网络间实现信息传输的协议簇 TCP(传输控制协议) 一种面向连接的、可靠的、基于字节流的传输层通信协议 IP(网际协议) 用于封包交换数据网络…

2024年最新阿里云服务器价格表_CPU内存+磁盘+带宽价格

2024年阿里云服务器租用费用,云服务器ECS经济型e实例2核2G、3M固定带宽99元一年,轻量应用服务器2核2G3M带宽轻量服务器一年61元,ECS u1服务器2核4G5M固定带宽199元一年,2核4G4M带宽轻量服务器一年165元12个月,2核4G服务…

YT8531调试记录

总结 还是从设备树,mac驱动,mac驱动对mdio总线的注册,phy驱动 ,phy的datasheet,cpu的datasheet 几个方面来看来看 0.确认供电,以及phy的地址(一般会有多个地址,根据相关引脚电平可配置) 1.确…

青年人格测验,管理岗和管培生的招聘测评

青年人格测验是由美国心理学家高夫提出,并逐渐完善的一种性格测试工具。在青年人格测验中,人的人格特质被分为许多个维度,不同的维度,都能对我们健全自身性格,了解自己,起着至关重要的作用。 高夫曾是mmpi…

[NKCTF 2024]web解析

文章目录 my first cms全世界最简单的CTF解法一解法二 my first cms 打开题目在最下面发现是CMS Made Simple,版本为2.2.19 扫一下发现存在后台登陆界面,直接访问 用字典爆破下admin的密码为Admin123 然后直接登录,去漏洞库搜一下其实存在…

武汉凯迪正大—开关柜模拟装置

产品简介 产品包括断路器模拟装置,控制器和控制盒,控制盒包括盒体,控制按钮和信号装置,控制按钮和信号装置设置于盒体的表面,且均与控制器连接.控制按钮和信号装置集中安装于控制盒的盒体表面上。 由于控制盒上的控制按钮和信号装置集中安装,使开关柜模拟实验控制装置体积小,…

mysql增量备份与修复

MySQL数据库增量恢复 1.一般恢复 将所有备份的二进制日志内容全部恢复 2.基于位置恢复 数据库在某一时间点可能既有错误的操作也有正确的操作 可以基于精准的位置跳过错误的操作 发生错误节点之前的一个节点,上一次正确操作的位置点停止 3.基于时间点恢复 跳过…

[自动化测试]博客系统测试用报告

目录 (一) 项目介绍 (二) 测试计划 1. 功能测试 (1)测试环境 (2)测试用例 (3)部分的功能测试 2. 自动化测试 项目介绍 项目整体基于 HTTP 协议,前端使用 HTMLCSSJS 构建页面整体布局&#x…

【Unity3D小功能】Unity3D中实现点击‘文字’出现‘UI面板’

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群:398291828 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 宠粉博主又来了,今天有粉丝问我如何实…

如何在VS Code上搭建 C/C++开发环境

顾得泉:个人主页 个人专栏:《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂,年薪百万! 一、什么是VScode VScode(Visual Studio Code)是一款由微软开发的免费开源的轻量级代码编辑器。它…

AI垃圾装满溢出识别摄像机

AI垃圾装满溢出识别摄像机是一种基于人工智能技术的创新设备,旨在实时监测公共场所垃圾箱的装填情况,及时警示相关部门进行清理或更换,提高城市管理效率,改善城市环境质量。这种AI垃圾装满溢出识别摄像机通过搭载先进的图像识别和…

大唐杯国赛解读及赛前准备分析

从参赛类别的区分到大唐杯名称由原来的“第九届‘大唐杯’全国大学生移动通信5G技术大赛”变更为“第九届‘大唐杯’全国大学生新一代信息通信技术大赛”一系列的变化。 接下来重点讲解本科A组的情况分析: 需要注意的是,和去年的比赛不同,今年并不是两人互相讨论,而是和省…

如何使用 JavaScript 导入和导出 Excel

前言 在现代的Web应用开发中,与Excel文件的导入和导出成为了一项常见而重要的任务。无论是数据交换、报告生成还是数据分析,与Excel文件的交互都扮演着至关重要的角色。本文小编将为大家介绍如何在熟悉的电子表格 UI 中轻松导入 Excel 文件,…

zabbix进阶

知识点补充 zabbix server在主机上运行服务,端口号为10050,zabbix agent 在被监控机器上运行(源码下载)主要完成对cpu,磁盘的信息采集,端口号为10051 zabbix 软件结构组成: 1.Zabbix Web GUI …

【Java程序设计】【C00369】基于(JavaWeb)Springboot的笔记记录分享平台(有论文)

[TOC]() 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,博客中有上百套程序可供参考,欢迎共同交流学习。 项目简介 项目获取 🍅文末点击…

常用类四(Math类)

目录 Math 类的常用方法: Random 类的常用方法: java.lang.Math 提供了一系列静态方法用于科学计算;其方法的参数和返回值类型一般 为 double 型。如果需要更加强大的数学运算能力,计算高等数学中的相关内容,可以使用…

【Java程序设计】【C00343】基于Springboot的船运物流管理系统(有论文)

基于Springboot的船运物流管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 🍅文末点击卡片获取源码🍅 开发环境 运行环境:推荐jdk1.8; 开发工具:eclipse以及i…