gin源码分析
一、高性能
-
使用sync.pool解决频繁创建的context对象,在百万并发的场景下能大大提供访问性能和减少GC
// ServeHTTP conforms to the http.Handler interface. // 每次的http请求都会从sync.pool中获取context,用完之后归还到pool中 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {c := engine.pool.Get().(*Context)c.writermem.reset(w) //重置 responsewriterc.Request = reqc.reset() //重置使用过的context各个属性engine.handleHTTPRequest(c)engine.pool.Put(c) }// 这里给pool指定了一个创建新对象的函数,注意不是所有的请求共用一个context,context在高并发场景下可能会分配多个,但是远远小于并发协程数量。 sync.pool.new是可能并发调用的,所以内部的逻辑需要保障线程安全 func New(opts ...OptionFunc) *Engine {... engine.RouterGroup.engine = engineengine.pool.New = func() any {return engine.allocateContext(engine.maxParams)}return engine.With(opts...) }//每次http请求都需要分配一个context,这个初始context初始化了两个数组的最大容量 func (engine *Engine) allocateContext(maxParams uint16) *Context {v := make(Params, 0, maxParams)skippedNodes := make([]skippedNode, 0, engine.maxSections)return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes} } -
前缀树路由(类似httprouter的路由,提升性能近40倍)
gin在v1.0版本开始放弃了
github.com/julienschmidt/httprouter,重新实现了一套路由;对比gin新实现的路由基本上采用了httprouter的逻辑,但是和框架结合的更加完整,比如说把httprouter中router的能力提到了engine中。 -
json序列化优化
gin提供了四种可选的json序列化方式,默认情况下会使用encoding/json
/github.com/gin-gonic/gin@v1.10.0/internal/json-- go_json.go ("github.com/goccy/go-json")-- json.go ("encoding/json")-- jsoniter.go ("github.com/json-iterator/go")-- sonic.go ("github.com/bytedance/sonic")需要在编译期间指定tag来决定使用哪种序列化工具
go run -tags={go_json|jsoniter|sonic|(不指定默认encoding)} main.go通过一个简单的基准测试看看哪种json序列化效率更高
package ganimport ("encoding/json"sonicjson "github.com/bytedance/sonic"gojson "github.com/goccy/go-json"itjson "github.com/json-iterator/go""testing" )func BenchmarkJson(b *testing.B) {jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`m := &map[string]interface{}{}for i := 0; i < b.N; i++ {json.Unmarshal([]byte(jsonStr), m)json.Marshal(m)} }func BenchmarkGOJson(b *testing.B) {jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`m := &map[string]interface{}{}for i := 0; i < b.N; i++ {gojson.Unmarshal([]byte(jsonStr), m)gojson.Marshal(m)} }func BenchmarkItJson(b *testing.B) {m := &map[string]interface{}{}jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`for i := 0; i < b.N; i++ {itjson.Unmarshal([]byte(jsonStr), m)itjson.Marshal(m)} }func BenchmarkSonicJson(b *testing.B) {m := &map[string]interface{}{}jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`for i := 0; i < b.N; i++ {sonicjson.Unmarshal([]byte(jsonStr), m)sonicjson.Marshal(m)} }测试结果: sonic > go_json > json_iterator > encoding
$ go test -bench='Json$' -benchtime=5s -benchmem . goos: windows goarch: amd64 pkg: gan cpu: Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz BenchmarkJson-12 2632854 2260 ns/op 616 B/op 24 allocs/op BenchmarkGOJson-12 5226374 1142 ns/op 248 B/op 11 allocs/op BenchmarkItJson-12 4811112 1260 ns/op 400 B/op 19 allocs/op BenchmarkSonicJson-12 6616218 913.0 ns/op 333 B/op 10 allocs/op PASS ok gan 30.313s
二、基于前缀树的路由设计
-
go语言中原生net/http包在负载路由下的缺陷
动态路由:缺少例如hello/:name,hello/*这类的规则。
鉴权:没有分组/统一鉴权的能力,需要在每个路由映射的handler中实现。 -
http请求怎么进入的gin的处理逻辑中去的?
-
gin框架中调用了net/http包,监听address,请求都会进入顶层路由,也就是engine结构实现的ServeHTTP函数中
func (engine *Engine) Run(addr ...string) (err error) {......debugPrint("Listening and serving HTTP on %s\n", address)err = http.ListenAndServe(address, engine.Handler())return } func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {......engine.handleHTTPRequest(c)...... } -
在engine.handleHTTPRequest© 中,根据request中的path,在路由表中获取路由对应的handler然执行
func (engine *Engine) handleHTTPRequest(c *Context) {httpMethod := c.Request.MethodrPath := c.Request.URL.Path......// Find root of the tree for the given HTTP methodt := engine.treesfor i, tl := 0, len(t); i < tl; i++ {if t[i].method != httpMethod {continue}root := t[i].root// Find route in treevalue := root.getValue(rPath, c.params, c.skippedNodes, unescape)if value.params != nil {c.Params = *value.params}if value.handlers != nil {c.handlers = value.handlersc.fullPath = value.fullPathc.Next() //最终执行的handlerc.writermem.WriteHeaderNow()return}......} }
-
-
路由的注册和匹配
-
Engine继承了RouterGroup的能力,当我们注册handler时,或者在分组路由下去注册handler时,其实都是调用的RouterGroup的能力
type Engine struct {RouterGroup...... } -
RouterGroup中保留了一个Engine指针,最终的路由是注册到了Engine中的路由表中,这个路由表没有设计在RouterGroup中,保证了RouterGroup的职责单一性,也就是只做路由分组。
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {......group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj() } -
Engine中保存了一个methodTree对象,这个就是路由表,是一个树状结构

type methodTree struct {method stringroot *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 }type methodTrees []methodTree
-
三、中间件执行流程
-
中间件执行链路
-
中间件保存在GroupRouter中
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {group.Handlers = append(group.Handlers, middleware...)return group.returnObj() } -
每声明一次分组,都会将handler进行一次合并
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {return &RouterGroup{Handlers: group.combineHandlers(handlers), //合并中间处理链路basePath: group.calculateAbsolutePath(relativePath),engine: group.engine,} } -
以上只是将中间件合并成了一条链路,最终的业务逻辑会在执行GET.POST等请求方法时,拼接到执行链路末端
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath := group.calculateAbsolutePath(relativePath)handlers = group.combineHandlers(handlers) //这里参数的handlers是业务逻辑,合并到链路末尾。group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj() } -
最终效果

-
-
前置处理和后置处理
-
假设需要对一段业务逻辑采集它的执行耗时,一般我们需要在执行逻辑前声明一个时间点,执行完后再用当前时间减去执行前的时间得到耗时。

-
在gin中的实现方式是通过context的next方法去实现的,结合上面的流程,在中间件中每声明一次next,执行链会先去执行下一个handler,最终的效果应该是ABBA方式的执行。
func (c *Context) Next() {c.index++for c.index < int8(len(c.handlers)) {c.handlers[c.index](c)c.index++} }
-
其他:
Gin的核心特性
- 高性能:Gin的HTTP请求处理速度极快,能够支持大量的并发连接(gin并不是基准测试中最快的框架,但是相对较快,简单好用,用户体量大)
- 简单易用:Gin的API设计直观,使得开发者可以快速上手并构建应用。
- 中间件支持:Gin允许开发者通过中间件来扩展其功能,这为构建复杂的Web应用提供了可能。
- 路由和参数绑定:Gin提供了强大的路由功能,支持参数绑定,使得URL处理更加灵活。
- 数据渲染:Gin支持多种数据渲染方式,包括JSON、XML、HTML等,方便开发者根据需求选择合适的输出格式。
参考:https://geektutu.com/post/gee-day4.html
相关文章:
gin源码分析
一、高性能 使用sync.pool解决频繁创建的context对象,在百万并发的场景下能大大提供访问性能和减少GC // ServeHTTP conforms to the http.Handler interface. // 每次的http请求都会从sync.pool中获取context,用完之后归还到pool中 func (engine *Engin…...
数学建模入门
目录 文章目录 前言 一、数学建模是什么? 1、官方概念: 2、具体过程 3、适合哪一类人参加? 4、需要有哪些学科基础呢? 二、怎样准备数学建模(必备‘硬件’) 1.组队 2.资料搜索 3.常用算法总结 4.论文撰写的…...
【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十二)-无人机群在物流中的应用
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...
同三维T80006EH2-4K30编码器视频使用操作说明书:高清HDMI编码器,高清SDI编码器,4K超清HDMI编码器,双路4K超高清编码器
同三维T80006EH2-4K30编码器视频使用操作说明书:高清HDMI编码器,高清SDI编码器,4K超清HDMI编码器,双路4K超高清编码器 T80006EH2-4K30编码器 同三维,十多年老品牌,我们一直专注:视频采集卡、视频…...
DHCP原理及配置
目录 一、DHCP原理 DHCP介绍 DHCP工作原理 DHCP分配方式 工作原理 DHCP重新登录 DHCP优点 二、DHCP配置 一、DHCP原理 1 DHCP介绍 大家都知道,现在出门很多地方基本上都有WIFI,那么有没有想过这样一个问题,平时在家里都是“固定”的…...
异步日志:性能优化的金钥匙
一、背景 2024 年 4 月的一个宁静的夜晚,正当大家忙完一天的工作准备休息时,应急群里“咚咚咚”开始报警,提示我们余利宝业务的赎回接口成功率下降。 通过 Monitor 监控发现,该接口的耗时已经超过了网关配置的超时阈值(2s)&#…...
matlab仿真 模拟调制(上)
(内容源自详解MATLAB/SIMULINK 通信系统建模与仿真 刘学勇编著第五章内容,有兴趣的读者请阅读原书) 1.幅度调制 clear all ts0.0025; %信号抽样时间间隔 t0:ts:10-ts;%时间矢量 fs1/ts;%抽样频率 dffs/length(t); %fft的频率分…...
【数据结构】--- 堆的应用
个人主页:星纭-CSDN博客 系列文章专栏 :数据结构 踏上取经路,比抵达灵山更重要!一起努力一起进步! 一.堆排序 在前一个文章的学习中,我们使用数组的物理结构构造出了逻辑结构上的堆。那么堆到底有什么用呢&…...
0基础学会在亚马逊云科技AWS上利用SageMaker、PEFT和LoRA高效微调AI大语言模型(含具体教程和代码)
项目简介: 小李哥今天将继续介绍亚马逊云科技AWS云计算平台上的前沿前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS上的AI软甲开发最佳实践,并应用到自己的日常工作里。本次介绍的是如何在Amazon SageMaker上…...
护网HW面试——redis利用方式即复现
参考:https://xz.aliyun.com/t/13071 面试中经常会问到ssrf的打法,讲到ssrf那么就会讲到配合打内网的redis,本篇就介绍redis的打法。 未授权 原理: Redis默认情况下,会绑定在0.0.0.0:6379,如果没有采用相关…...
C++ //练习 15.8 给出静态类型和动态类型的定义。
C Primer(第5版) 练习 15.8 练习 15.8 给出静态类型和动态类型的定义。 环境:Linux Ubuntu(云服务器) 工具:vim 解释 静态类型:在编译时已知,是在变量声明时的类型或表达式生成的…...
阿里云ECS服务器安装jdk并运行jar包,访问成功详解
安装 OpenJDK 8 使用 yum 包管理器安装 OpenJDK 8 sudo yum install -y java-1.8.0-openjdk-devel 验证安装 安装完成后,验证 JDK 是否安装成功: java -version设置 JAVA_HOME 环境变量: 为了确保系统中的其他应用程序可以找到 JDK&…...
Windows系统上使用npm来安装和配置Yarn,在VSCode中使用
一、安装Yarn 1. 安装Node.js和npm 如果还没有安装Node.js和npm,可以从Node.js官方网站下载并安装最新版本的Node.js,npm会随Node.js一起安装。 2. 使用npm安装Yarn 打开命令提示符或PowerShell,运行以下命令来全局安装Yarn: …...
Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理
Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理 目录 Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理 一、简单介绍 二、在Unity中设置颜色空间 三、Unity中的Gamma…...
JavaScript的学习(二)
今天继续学习JavaScript的第二天,还是打基础 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…...
【接口自动化_06课_Pytest+Excel+Allure完整框架集成】
一、logging在接口自动化里的应用 1、设置日志的配置,并收集日志文件 日志的设置需要在pytest.ini文件里设置。这个里面尽量不要有中文 2、debug日志的打印 pytest.ini文件的开关一定得是true才能在控制台打印日志 import allure import pytest from P06_PytestFr…...
Profibus协议转Profinet协议网关模块连接智能电表通讯案例
一、背景 在工业自动化领域,Profibus协议和Profinet协议是两种常见的工业通讯协议,而连接智能电表需要用到这两种协议之间的网关模块。本文将通过一个实际案例,详细介绍如何使用Profibus转Profinet模块(XD-PNPBM20)实…...
【学习笔记】无人机(UAV)在3GPP系统中的增强支持(九)-无人机服务区分离
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...
acrobat 中 PDF 复制时不能精确选中所选内容所在行的一种解决方法
现象:划取行的时候,自动扩展为多行 如果整段选中复制,粘贴后是乱码 解决步骤 识别完,保存 验证 可以按行复制了。 如果遇到仅使用 acrobat OCR 不能彻底解决的,更换其他自己熟悉的进行 OCR。...
安卓学习中遇到的问题【bug】
安卓学习中遇到的问题 1Gradle下载慢怎么办? Gradle下载慢怎么办? distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-7.5-bin.zip 2 Could not resolve all files for configuration ‘:classpath‘. > Could not resolv…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
