看看Gin框架是如何实现的
基本使用
gin是一个高性能的golang web框架,它的代码其实很少,相对于spring来说,搞懂gin真是砍瓜切菜
先来看一下gin的基础用法
import "github.com/gin-gonic/gin"func Init() {r := gin.Default()initRoute(r)r.Run()
}func initRoute(r *gin.Engine) {r.GET("ping", handler)
}func handler(ctx *gin.Context) {ctx.String(200, "Hello World!")
}
这个例子里,我们启动了一个简单的web服务,使用gin.Default
创建一个Engine对象,Engine是Gin核心中的核心,我们后面会具体讲解。之后通过initRoute注册一个路由,当访问localhost:8080/ping的时候,就会返回Hello World!。
最后调用r.run让服务启动。只需要很少的几行代码,就能通过gin启动一个web服务。我们主要探索它的实现原理,关于gin更多的语言特性和用法这里就不细说了,更多例子可以前往Gin Examples.
Engine
官网解释:Engine是框架的实例,它包含了复用器、中间件和各类配置。
type Engine struct {// 路由组RouterGroup// 如果设置为true,如果/foo/没有匹配到路由,自动重定向到/fooRedirectTrailingSlash bool// 和RedirectTrailingSlash类似,对path做一些修正RedirectFixedPath boolHandleMethodNotAllowed boolForwardedByClientIP bool// DEPRECATEDAppEngine bool// 是否通过url.RawPath来获取参数UseRawPath bool// If true, the path value will be unescaped.// If UseRawPath is false (by default), the UnescapePathValues effectively is true,// as url.Path gonna be used, which is already unescaped.UnescapePathValues boolRemoveExtraSlash bool// List of headers used to obtain the client IP when// `(*gin.Engine).ForwardedByClientIP` is `true` and// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.RemoteIPHeaders []string// If set to a constant of value gin.Platform*, trusts the headers set by// that platform, for example to determine the client IPTrustedPlatform string// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm// method call.MaxMultipartMemory int64delims render.DelimssecureJSONPrefix stringHTMLRender render.HTMLRenderFuncMap template.FuncMapallNoRoute HandlersChainallNoMethod HandlersChainnoRoute HandlersChainnoMethod HandlersChainpool sync.Pooltrees methodTreesmaxParams uint16maxSections uint16trustedProxies []stringtrustedCIDRs []*net.IPNet
}
Engine里面这么多字段,还有很多方法没往这贴呢,不用着急,我们一点一点的抽丝剥茧,揭开gin的真面目
简单的看,一个gin服务对应一个Engine实例。r = gin.Default()
实际上就是创建了一个Engine对象,它的源码也不复杂
func Default() *Engine {engine := New()// 注册中间件engine.Use(Logger(), Recovery())return engine
}func New() *Engine {engine := &Engine{RouterGroup: RouterGroup{Handlers: nil,basePath: "/",root: true,},FuncMap: template.FuncMap{},RedirectTrailingSlash: true,......}engine.RouterGroup.engine = engine// pool存放context对象engine.pool.New = func() interface{} {return engine.allocateContext()}return engine
}
首先New一个Engine出来,初始化一些基础结构,然后通过Use方法注入两个中间件logger和recovery,它们的类型是HandlerFunc,logger打日志用的,recovery用来在服务panic是recover掉,然后返回500,避免服务直接崩溃。
有意思的是engine里面有一个字段pool,类型是sync.pool,在创建Engine的时候,我们也对pool进行了初始化,关于sync.pool可以看我的另一片文章【todo】,可以看到pool的New方法创建并返回了一个context,而context是每一个请求都有的,携带上下文信息的对象,可以想象context是非常重的,如果每一个请求都创建并销毁一个context对象,GC肯定撑不住了啊,还怎么成为一个高性能的wen服务器?所以Gin通过sync.pool来达成对context的复用,优化GC。
路由
不管web服务框架是如何实现的,它最基础的功能就是路由,简单来说就是解析url,然后分派不同的handler来处理对应请求。
在创建Engine后,我们还的告诉它,如何处理路由信息。注册路由非常简单,Engine支持RESTful的6种方法。
func initRoute(r *gin.Engine) {r.GET("ping", pingHandler)
}
实际上Engine是通过继承了RouterGroup(可以在Engine的结构体里找到)来实现路由处理的,所有的路由处理都在RouterGroup中完成,以Get源码为例
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {return group.handle(http.MethodGet, relativePath, handlers)
}
当我们通过GET注册路由后,会得到一个IRoutes对象,IRoutes本身是一个interface,定义了一系列方法,其中就包含GET,POST…等,不难发现RouterGroup实现了IRoutes。另外注册路由时,对一个path,不只可以穿入一个处理方法,源码里三个点代表我们可以传入多个handler,之后在请求到达时会逐个被调用。而GET直接调用了内部的group.handle,这里面又是如何实现的呢
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath := group.calculateAbsolutePath(relativePath)handlers = group.combineHandlers(handlers)group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj()
}
一共有三步
1、计算绝对路径
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {return joinPaths(group.basePath, relativePath)
}
其实就是字符串join一下,用basePath和当前注册的path组合。哪里来的basePath,以及为什么要join呢?
我们在创建Engine的时候,其实就注册了一个path,它就是"/",其他后续所有注册的path都挂在它下面,如果把它看成一颗树的root节点,在它下面还有更多的子节点,而一个完整的path,就是从根节点到子节点中间经过的所有Path的组合。实际上Gin对于路由的管理就用到了树的结构,这个后面再讲。
2、添加handlers
当前传进去的handlers是path的,但人家basePath也有handlers,basePath是path的前缀,当你发起请求的时候,是不是所有basePath的handlers都应该过一遍呢。添加的过程也并不负责,就是重新开辟一个数组空间,然后把basePath和path的handlers都拷贝到这里,然后返回数组地址。
3、关联绝对路径和handlers
这一步比较重要,逻辑也比较复杂了,先看看最外层的代码,滤去一些无关痛痒的代码
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {......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)......
}
首先根据method获取根节点,metod有哪些?GET, POST, PUT, DELETE…
如果根节点不存在,就创建一个,然后加入到entine.trees里,entine.trees是methodTrees,methodTrees的定义是[]methodTree,所以entine.trees就是一个methedTree数组。抽丝剥茧,methedTree又是什么玩意儿?
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
}
链路稍微有点长了,画个图更清晰些
未完待续…
看看Gin框架是如何实现的相关推荐
- Gin 框架学习笔记(03)— 输出响应与渲染
在 Gin 框架中,对 HTTP 请求可以很方便有多种不同形式的响应.比如响应为 JSON . XML 或者是 HTML 等. Context 的以下方法在 Gin 框架中把内容序列化为不同类型写 ...
- Gin 框架学习笔记(02)— 参数自动绑定到结构体
参数绑定模型可以将请求体自动绑定到结构体中,目前支持绑定的请求类型有 JSON .XML .YAML 和标准表单 form数据 foo=bar&boo=baz 等.换句话说,只要定义好结构体, ...
- gin框架长连接_gin框架教程一: go框架gin的基本使用
gin框架教程代码地址: 我们在用http的时候一般都会用一些web框架来进行开发,gin就是这样的一个框架,它有哪些特点呢 一:gin特点 1.性能优秀 2.基于官方的net/http的有限封装 3 ...
- go语言学习(二)——Gin 框架简介
GoWeb开发有很多框架,如Beego.Gin.Revel.Echo.IRis等,学习框架可以快速做开发,对比常见goweb框架,通过其github的活跃度,维护的team,生产环境中的使用率以及师兄 ...
- gin框架502错误
某次gin框架后台服务,请求报502错误.日志提示write timeout,是指服务端在回复客户端时超时了,让程序挂掉,还出现了堆栈打印.其根本原因是,后台处理逻辑耗时超过了上层调用,解决办法是优化 ...
- Golang 的Gin框架入门教学
学习Golang差不多有一个星期时间,开始自己做点小功能,练练手. Gin 介绍 Gin 是一个 Golang 写的 web 框架,具有高性能的优点,,基于 httprouter, 它提供了类似mar ...
- windows环境搭建golang的gin框架简易教程
第一步:安装golang 下载go1.16.7.windows-amd64.msi安装程序,按照界面提示安装 第二步:配置golang代理库地址(此步可忽略) set GOPROXY=https:// ...
- go系列之利用Gin框架获取form参数
利用Gin框架获取form参数 除了通过URL查询参数提交数据到服务器外,常用的还有通过Form表单的方式.Form表单相比URL查询参数,用户体验好,可以承载更多的数据,尤其是文件上传,所以也更为方 ...
- gin ajax 获取请求参数,go的gin框架从请求中获取参数的方法
前言: go语言的gin框架go里面比较好的一个web框架, github的start数超过了18000.可见此框架的可信度 如何获取请求中的参数 假如有这么一个请求: POST /post/te ...
- gin 编译路径错误_[系列] Gin框架 - 自定义错误处理
概述 很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上,地址:https://github.com/xinliangnote/Go 开始 ...
最新文章
- [转] asp.net core Introducing View Components
- C++ Primer笔记12_运算符重载_递增递减运算符_成员訪问运算符
- python3.8爬虫_python爬虫系列(3.8-正则的使用)
- mysql 的client_mysql(一)-客户端Client相关
- ipmitool 设置网关_Linux下使用命令行配置IPMI
- 网络性能测试之pathrate的安装使用
- 【每日算法Day 77】LeetCode 第 181 场周赛题解
- python装饰器Decorators
- Asp.Net WebAPI传递json对象、后台手动接收参数
- 读王小波先生的《黄金时代》、《青铜时代》
- LaTeX新手半小时速成手册(不速成你打我
- golang runtime.Caller 学习笔记
- PPT常用快捷键汇总
- java pdf转html插件pdf2htmlex
- 用ZBrush做游戏建模,3D打印,手办模型、珠宝设计等
- 蓝桥杯嵌入式STM32G431——第九届省赛真题电子定时器
- 【必看】论文写作入门技巧
- 关于直播的技术细节都在这里
- 【小睿精选·第八期】为NBA球员提供预警新冠肺炎的智能戒指Oura Ring
- 道闸系统服务器功能,道闸系统_停车场自动道闸系统 - 九鼎智能
热门文章
- 浙大计算机学院博士生读几年,中国200万在读硕士生和博士生,每月能拿到多少补助?...
- 电路中VCC、VDD、VEE和VSS的区别
- 爱婴室主要股东再现减持:莫锐伟、王云亦是如此,业绩表现不理想
- Config语言与Config.in文件
- 网易后端二面经验分享
- 小程序:Thu May 05 2022 11:03:00 GMT+0800 (中国标准时间) 渲染层错误
- 【荐号】有了它们,成功创业,成就事业巅峰,迎娶白富美,指日可待!
- X509V3数字证书介绍
- Oracle内存过度消耗风险提醒
- 热血传奇技术的一些基础知识