Gin框架源码解析
概要
目录
Gin路由详解
Gin框架路由之Radix Tree
一、路由树节点
二、请求方法树
三、路由注册以及匹配
中间件含义
Gin框架中的中间件
主要讲述Gin框架路由和中间件的详细解释。本文章将从Radix树(基数树或者压缩前缀树)、请求处理、路由方法树、路由的注册与匹配以及中间件的详细解释这五大部分入手。
Gin 框架 路由使用前缀树,路由注册的过程就是构造前缀树的过程,路由匹配的过程是查找前缀树 的过程。
Gin路由详解
Gin框架使用的是定制版本的httprouter。我们简单介绍下关于httprouer框架。
Httprouter是一个高性能路由分发器,它负责将不同方法的多个路径分别注册到各个handle函数。当收到请求时,Httprouter会快速查找请求的路径是否有相对应的处理函数,并执行相应的业务逻辑处理。
Httprouter使用基数树(radix tree)来进行高效的路径查找,这种数据结构适用于需要快速查找和匹配的场景。此外,Httprouter还支持两种通配符匹配,使得路由规则更加灵活和强大。
它为Gin提供了高效、灵活的路由匹配功能,使得Gin成为了一个高性能的Web框架。同时,Httprouter也是Go语言中广泛使用的一个路由库,独立于Gin框架,可以被其他Go语言的Web框架所使用。
Gin框架路由之Radix Tree
Radix Tree 是一种更节省空间的前缀树。对于基数树的每个节点,如果该节点是唯一的子树的话,就和父节点合并。
Radix Tree 可以被认为是一个简洁版的前缀树。我们注册路由的过程就是在构造前缀树的过程,具有公共前缀的节点也共享一个公共父节点。
如下图所示:GET方法对应的路由树。
- Priority(优先级):每个树级别上的子节点都按照优先级排序,其中优先级就是在子节点上注册的句柄数量。优点:优先匹配被大多数路由路径包含的节点(更快速定位)、最长路径可以优先匹配(成本补偿)。
- Handle: Get每个路由对应的实现函数。*<数字> 表示Handle处理函数的内存地址。
URL具有层级结构,并且都是有限的字符组,所以有很多常见的前缀。这样是的我们很容易将路由简化为更小的问题。
路由器为每种请求方法管理一颗单独的树。
一、路由树节点
type node struct {// 节点路径,比如上面的s,earch,和upportpath string// 和children字段对应, 保存的是分裂的分支的第一个字符// 例如search和support, 那么s节点的indices对应的"eu"// 代表有两个分支, 分支的首字母分别是e和uindices string// 儿子节点children []*node// 处理函数链条(切片)handlers HandlersChain// 优先级,子节点、子子节点等注册的handler数量priority uint32// 节点类型,包括static, root, param, catchAll// static: 静态节点(默认),比如上面的s,earch等节点// root: 树的根节点// catchAll: 有*匹配的节点// param: 参数节点nType nodeType// 路径上最大参数个数maxParams uint8// 节点是否是参数节点,比如上面的:postwildChild bool// 完整路径fullPath string
}
二、请求方法树
每一个HTTP method都对应一颗radix树,我们注册路由的时候都会调用addRoute函数。函数含义我们可以看到在注册路由的时候都是先根据请求方法获取对应的树,也就是gin框架会为每一个请求方法创建一颗对应的树。只不过需要注意一个细节是gin框架中请求方法对应树关系并不是使用map而是使用的切片,engine.trees的类型是methodThrees。
type methodTree struct {method stringroot *node
}type methodTrees []methodTree // slicefunc (engine *Engine) addRoute(method, path string, handlers HandlersChain) {// liwenzhou.com...// 获取请求方法对应的树root := engine.trees.get(method)if root == nil {// 如果没有就创建一个root = new(node)root.fullPath = "/"engine.trees = append(engine.trees, methodTree{method: method, root: root})}root.addRoute(path, handlers)
}
考点 :为什么用切片存储请求方法树,而不用map?
节省内存。HTTP请求方法数量也就9种,用切片存储和查询效率足够。只需要做出一次性内存申请即可。
三、路由注册以及匹配
路由注册函数:
- addRoute函数:将具有给定句柄的节点添加到路径中。不是并发安全的函数。
- insertChild函数:根据path本身进行分割,将/分开的部分分别作为节点保存,形成一颗树结构。参数匹配中的 : 和 * 的区别是,前者是匹配一个字段,后者是匹配后面所有的路径。
路由匹配:
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {// 这里使用了对象池c := engine.pool.Get().(*Context)// 这里有一个细节就是Get对象后做初始化c.writermem.reset(w)c.Request = reqc.reset()engine.handleHTTPRequest(c) // 我们要找的处理HTTP请求的函数engine.pool.Put(c) // 处理完请求后将对象放回池子
}func (engine *Engine) handleHTTPRequest(c *Context) {// 根据请求方法找到对应的路由树t := engine.treesfor i, tl := 0, len(t); i < tl; i++ {if t[i].method != httpMethod {continue}root := t[i].root// 在路由树中根据path查找value := root.getValue(rPath, c.Params, unescape)if value.handlers != nil {c.handlers = value.handlersc.Params = value.paramsc.fullPath = value.fullPathc.Next() // 执行函数链条c.writermem.WriteHeaderNow()return}c.handlers = engine.allNoRouteserveError(c, http.StatusNotFound, default404Body)
}
路由匹配是由节点的GetValue方法实现的。getValue根据给定的路径返回nodeValue值,里面保存的处理函数和匹配到的路径参数数据。如果找不到任何处理函数,会尝试TSR(尾随斜杠重定向)。
中间件含义
中间件是指处理HTTP请求的函数或组件,通常用于在请求到达目标处理程序之前或之后执行一些处理逻辑。中间件在Go语言中经常被使用,因为它提供了一种可扩展和可重用的机制,用于处理身份验证、授权、日志记录、错误处理等常见的Web应用程序需求。
中间件在Go语言主要是因为它提供了一种灵活、可扩展和可重用的机制,用于处理Web应用程序中的各种需求和逻辑。
中间件优势:
- 函数式编程思想:Go语言支持函数式编程风格,而中间件提供了一种将函数作为参数传递并在请求处理过程中进行组合和调用的机制。使得代码更加模块化和可重用性。
- 请求处理流程的可扩展性:中间件允许开发人员将请求处理流程分解为多个独立的函数或组件,这些 组件可以按照特定的顺序组合和调用。这种可扩展性使得开发人员可以轻松的添加、移除或替换中间件,以满足特定的应用程序需求。
- 前后置处理逻辑:中间件可以在请求到达目标处理程序之前或之前执行一些处理逻辑,例如身份验证、日志记录、错误处理等。这种机制使得开发人员可以轻松的请求处理过程中添加前后置处理逻辑,而无需修改现有的代码。
- 社区支持和普及。
Gin框架中的中间件
gin框架中涉及中间件相关有4个常用的方法,c.Next() 、c.Abort() 、c.Set()、c.Get()
Gin中间件函数和处理函数是以切片的形式调用链条存在的,我们可以顺序调用也可以借助c.Next函数方法实现嵌套调用。
c.Set()
和c.Get()
这两个方法多用于在多个函数之间通过c
传递数据的,比如我们可以在认证中间件中获取当前请求的相关信息(userID等)通过c.Set()
存入c
,然后在后续处理业务逻辑的函数中通过c.Get()
来获取当前请求的用户。c
就像是一根绳子,将该次请求相关的所有的函数都串起来了。
c.Abort()
中断整个调用链条,从当前函数返回。
相关文章:

Gin框架源码解析
概要 目录 Gin路由详解 Gin框架路由之Radix Tree 一、路由树节点 二、请求方法树 三、路由注册以及匹配 中间件含义 Gin框架中的中间件 主要讲述Gin框架路由和中间件的详细解释。本文章将从Radix树(基数树或者压缩前缀树)、请求处理、路由方法树…...
MacOS设置JAVA_HOME环境变量
首先先查看一下,系统当前使用的java是谁,可以使用/usr/libexec/java_home命令 % /usr/libexec/java_home /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home检查一下这个路径下的文件,发现这是一个jre的目录。加上-V参数看…...
闭眼检测实现
引言 这段代码是一个实时眼睛状态监测程序,可以用于监测摄像头捕获的人脸图像中的眼睛状态,判断眼睛是否闭合。具体应用实现作用说明如下: 1. 实时监测眼睛状态 通过摄像头捕获的实时视频流,检测人脸关键点并计算眼睛的 EAR&a…...
系列六、Java垃圾回收器主要有哪些?
一、Java垃圾回收器主要有哪些? UseSerialGC、UseParallelGC、UseConcMarkSweepGC、UseParallelNewGC、UseParallelOldGC、UseG1GC...

【7】Spring Boot 3 集成组件:缓存组件 spring cache + spring data redis
目录 【7】Spring Boot 3 集成组件:缓存组件 spring cache spring data redis什么是缓存抽象声明式注解JSR-107对应SpEL上下文数据 引入依赖cache 支持的缓存类型缓存类型配置NONESIMPLEREDIS自定义配置 CAFFEINE Hazelcast...总结 个人主页: 【⭐️个人主页】 需要…...
说说Java中的不可重入锁
什么是锁? 简单来讲在Java中,锁是一种用于并发控制的机制,用于保护共享资源,防止多个线程同时访问或修改数据导致的数据不一致性和线程安全问题。在Java虚拟机(JVM)中,每个对象都有一个相关联的…...
C++学习 --vector
目录 1, 什么是vector 2, 创建vector 2-1, 标准数据类型 2-2, 自定义数据类型 2-3, 其他创建方式 3, 操作vector 3-1, 赋值 3-2, 添加元素 3-2-1, 添加元素(assi…...
Android图片涂鸦,Kotlin(1)
Android图片涂鸦,Kotlin(1) import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.PointF import android.…...

upload-labs(1-17关攻略详解)
upload-labs pass-1 上传一个php文件,发现不行 但是这回显是个前端显示,直接禁用js然后上传 f12禁用 再次上传,成功 右键打开该图像 即为位置,使用蚁剑连接 连接成功 pass-2 源码 $is_upload false; $msg null; if (isse…...
《 机器人基础 》期末试卷(A)
一、填空题(30分,每空2分) 1. 按照相机的工作方式,机器人常用相机分为1)__ 单目摄像头 2)__ 双目摄像头 _ 3)_深度摄像头_ 三类。 2. 度量地图强调…...

Azure Machine Learning - Azure AI 搜索中的矢量搜索
矢量搜索是一种信息检索方法,它使用内容的数字表示形式来执行搜索方案。 由于内容是数字而不是纯文本,因此搜索引擎会匹配与查询最相似的矢量,而不需要匹配确切的字词。本文简要介绍了 Azure AI 搜索中的矢量支持。 其中还解释了与其他 Azure…...
3 redis实现一个消息中间件
使用list实现一个队列,可以从左侧入队,也可以从右侧入对 即可以从左侧读取,也可以从右侧读取 1、Lindex Lindex 命令用于通过索引获取列表中的元素 也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的…...

js添加dom到指定div之后,并给添加的dom类名,然后设置其样式,以及el-popover层级z-index过高问题解决。
遇到一个需求,Vue项目做一个表格,要求表头与表格内容分开,如下效果所示,表头与表格有个高度间隔边距(箭头所示),因为默认我们的el-table的表头与内容是一起的: 思路:通过querySelector获取el-table__header-wrapper元素,通过createElement创建一个div,通过 newElem…...
C语言结构体
#include <stdio.h> #include <string.h> #include <stdlib.h>//struct Student_s { // int num; // char name[20]; // char gender; // int age; // float Chinese; // float Math; // float English; // char addr[30]; //}; //最后的分号一定要写&#x…...

【Python大数据笔记_day10_Hive调优及Hadoop进阶】
hive调优 hive官方配置url: Configuration Properties - Apache Hive - Apache Software Foundation hive命令和参数配置 hive参数配置的意义: 开发Hive应用/调优时,不可避免地需要设定Hive的参数。设定Hive的参数可以调优HQL代码的执行效率,或帮助定位问…...

React经典初级错误
文章 前言错误场景问题分析解决方案后言 前言 ✨✨ 他们是天生勇敢的开发者,我们创造bug,传播bug,毫不留情地消灭bug,在这个过程中我们创造了很多bug以供娱乐。 前端bug这里是博主总结的一些前端的bug以及解决方案,感兴…...
C# System.Array.CopyTo() 和 System.Array.Clone() 有什么区别
System.Array.CopyTo() 和 System.Array.Clone() 是用于数组复制的两种不同方法,它们在实现和用途上有一些区别。 System.Array.CopyTo() 方法: CopyTo() 方法用于将数组的元素复制到另一个数组。它是 Array 类的实例方法,可以用于复制一个…...
Stable Diffusion 启动时 got an unexpected keyword argument ‘socket_options‘ 错误解决
Stable Diffusion 启动时 got an unexpected keyword argument socket_options 错误解决 问题解决方法 问题 Launching Web UI with arguments: Traceback (most recent call last):File "launch.py", line 48, in <module>main()File "launch.py"…...
CSS 文本属性篇
文字颜色 属性名:color作用:控制文字的颜色可选值: 1.颜色名 color: blue; 2.rgb或rgba color:rgb(132, 220, 254); color:rgba(132, 220, 254,0.5); 3.hex或hexa(十六进制) color:#0078d4; color:#0078d48b; 4.hsl或h…...
Activiti,Apache camel,Netflex conductor对比,业务选型
Activiti,Apache camel,Netflex conductor对比,业务选型 1.activiti是审批流,主要应用于人->系统交互,典型应用场景:请假,离职等审批 详情可见【精选】activti实际使用_activiti通过事件监听器实现的优势_记录点滴…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

Web APIS Day01
1.声明变量const优先 那为什么一开始前面就不能用const呢,接下来看几个例子: 下面这张为什么可以用const呢?因为复杂数据的引用地址没变,数组还是数组,只是添加了个元素,本质没变,所以可以用con…...
Steam爬取相关游戏评测
## 因为是第一次爬取Steam。所以作为一次记录发出;有所错误欢迎指出。 无时间指定爬取 import requests import time import csv import osappid "553850" # 这里你也可以改成 #appid int(input()) max_reviews 10000 # 想爬多少条 # max_reviews…...
Qt Quick模块功能及架构
Qt 6.0 中的 Qt Quick 模块是构建现代、动态用户界面的核心框架,基于声明式编程(QML)和 JavaScript,专注于高性能、流畅的动画和跨平台 UI 开发。、 一、主要功能改进 1. Qt Quick 核心架构 QML 引擎升级:Qt 6.0 使用…...
Java编程之组合模式
引言 在软件开发的世界里,我们经常会遇到需要表示"部分-整体"层次结构的场景。比如文件系统中的文件和文件夹、图形界面中的各种组件、企业组织架构中的部门和员工等。这些场景都有一个共同的特点:我们需要以一种统一的方式来处理单个对象和由…...