Gin框架原理
标准库net/http、Gin框架原理
[TOC]
标准库net/http
demo:
定义了一个路由 /hello,绑定了一个handler,输出当前path,ListenAndServe方法启动web服务,第一个参数表示监听的端口,第二个参数代表 处理所有的HTTP请求 的实例,等于nil时会使用默认的DefaultServeMux。
该demo是基于标准库实现的Web框架入口。
func main() {
http.HandleFunc("/hello", func (w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
// http.ListenAndServe()方法第二个参数的实现:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// 注册handler默认使用DefaultServeMux,与ListenAndServe第二个参数使用的处理器一致。
// 将path和对应的handler方法保存在DefaultServeMux的map中,等请求进来进行匹配处理。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}http.ListenAndServe是整个web的总流程,本质上还是走socket那一套,bind -》 listen -》 accept -》 read、write -》 close。
Gin对net/http的封装
Gin做的,就是实现这个Handler接口,掌管所有HTTP请求,提供丰富的能力:如路由分发、请求上下文的复用、中间件调用链等功能。
so,本质上也可以说gin是一个http router,主要看gin.Default()方法返回的engin,由gin的engin来进行请求封装、路由匹配、转发到对应的handler处理。
路由
在 Gin 框架中,路由规则被分成了最多 9 棵前缀树,每一个 HTTP Method对应一棵「前缀树」,树的节点按照 URL 中的 / 符号进行层级划分,URL 支持 :name 形式的名称匹配,还支持
*subpath形式的路径通配符。每个节点都会挂一系列的handler组成的处理链来处理匹配到的请求。
之所以要使用前缀树,是为了支持如
/hello/:name这种动态路由,路径上的每一段/{名字}作为前缀树的一个节点,通过树结构查询,如果中间某一层节点不匹配,则直接结束查询。
中间件
gin的中间件指的是handler方法,所有的中间件包括请求处理的handler都要满足这种方法签名:
跟着路由一起注册后,调用RouterGroup的combineHandlers方法,将这些handler组成一个HandlersChain,本质上是[]HandlerFunc,当请求进来时,通过路径找到对应的HandlersChain,注入到context中,调用Next方法处理请求。
在gin中,context的Next方法是会增加HandlersChain索引值,执行下一个方法,而Abort方法是直接更新HandlersChain索引值到一个比较大的数字,使得循环调用结束, Abort() 方法并不是通过 panic 的方式中断执行流,执行 Abort() 方法之后,当前函数内后面的代码逻辑还会继续执行。
gin就是通过context的Next方法和Abort方法,使得执行HandlersChain方法时可以嵌套执行:先从前往后顺序执行HandlersChain中在Next方法前的逻辑,直到最后一个,然后再从后往前执行Next方法后的逻辑。
gin.Context
主要是包装请求
*http.Request、响应http.ResponseWriter,让用户无需知道太多细节,比如消息头、消息体、状态码,消息类型等,直接提供现成包装好的方法比如context.Json();Context 会随着每一个请求的出现而产生,请求的结束而销毁,和当前请求强相关的信息都应由 Context 承载,比如动态路由
/hello/:name中的name参数;为处理请求的handler方法提供统一的入参,使得context可以持有处理请求的一系列handler方法,实现中间件处理逻辑;
参考
Last updated